chatgraph 0.1.0__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 +20 -0
- chatgraph/auth/credentials.py +33 -0
- chatgraph/bot/chatbot_app.py +155 -0
- chatgraph/bot/chatbot_model.py +117 -0
- chatgraph/bot/chatbot_router.py +58 -0
- chatgraph/error/chatbot_error.py +17 -0
- chatgraph/error/route_error.py +7 -0
- chatgraph/messages/__init__.py +5 -0
- chatgraph/messages/base_message_consumer.py +8 -0
- chatgraph/messages/rabbitMQ_message_consumer.py +78 -0
- chatgraph/types/message_types.py +13 -0
- chatgraph/types/output_state.py +15 -0
- chatgraph/types/route.py +33 -0
- chatgraph/types/user_state.py +26 -0
- chatgraph-0.1.0.dist-info/LICENSE +21 -0
- chatgraph-0.1.0.dist-info/METADATA +15 -0
- chatgraph-0.1.0.dist-info/RECORD +18 -0
- chatgraph-0.1.0.dist-info/WHEEL +4 -0
chatgraph/__init__.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from .auth.credentials import Credential
|
|
2
|
+
from .bot.chatbot_model import ChatbotApp
|
|
3
|
+
from .bot.chatbot_router import ChatbotRouter
|
|
4
|
+
from .messages.rabbitMQ_message_consumer import RabbitMessageConsumer
|
|
5
|
+
from .types.message_types import Message
|
|
6
|
+
from .types.output_state import ChatbotResponse, RedirectResponse
|
|
7
|
+
from .types.route import Route
|
|
8
|
+
from .types.user_state import SimpleUserState
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
'ChatbotApp',
|
|
12
|
+
'Credential',
|
|
13
|
+
'SimpleUserState',
|
|
14
|
+
'Message',
|
|
15
|
+
'ChatbotRouter',
|
|
16
|
+
'ChatbotResponse',
|
|
17
|
+
'RedirectResponse',
|
|
18
|
+
'RabbitMessageConsumer',
|
|
19
|
+
'Route',
|
|
20
|
+
]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Credential:
|
|
5
|
+
def __init__(
|
|
6
|
+
self, username: str | None = None, password: str | None = None
|
|
7
|
+
):
|
|
8
|
+
self.__username = username
|
|
9
|
+
self.__password = password
|
|
10
|
+
|
|
11
|
+
@property
|
|
12
|
+
def password(self):
|
|
13
|
+
if not self.__password:
|
|
14
|
+
raise ValueError('Senha vazia!')
|
|
15
|
+
return self.__password
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def username(self):
|
|
19
|
+
if not self.__username:
|
|
20
|
+
raise ValueError('Usuário vazio!')
|
|
21
|
+
return self.__username
|
|
22
|
+
|
|
23
|
+
@classmethod
|
|
24
|
+
def dot_env_credentials(
|
|
25
|
+
cls, user_env: str = 'CHATBOT_USER', pass_env: str = 'CHATBOT_PASS'
|
|
26
|
+
) -> 'Credential':
|
|
27
|
+
username = os.getenv(user_env)
|
|
28
|
+
password = os.getenv(pass_env)
|
|
29
|
+
|
|
30
|
+
if not username or not password:
|
|
31
|
+
raise ValueError('Corrija as variáveis de ambiente!')
|
|
32
|
+
|
|
33
|
+
return cls(username=username, password=password)
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import json
|
|
3
|
+
from functools import wraps
|
|
4
|
+
from logging import debug
|
|
5
|
+
|
|
6
|
+
import pika
|
|
7
|
+
import pika.exceptions
|
|
8
|
+
|
|
9
|
+
from ..auth.credentials import Credential
|
|
10
|
+
from ..error.chatbot_error import ChatbotError, ChatbotMessageError
|
|
11
|
+
from ..types.message_types import Message
|
|
12
|
+
from ..types.user_state import SimpleUserState, UserState
|
|
13
|
+
from .chatbot_router import ChatbotRouter
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ChatbotApp:
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
amqp_url: str,
|
|
20
|
+
queue_consume: str,
|
|
21
|
+
credentials: Credential,
|
|
22
|
+
user_state: UserState,
|
|
23
|
+
prefetch_count: int = 1,
|
|
24
|
+
virtual_host: str = '/',
|
|
25
|
+
):
|
|
26
|
+
self.__virtual_host = virtual_host
|
|
27
|
+
self.__prefetch_count = prefetch_count
|
|
28
|
+
self.__user_state = user_state
|
|
29
|
+
self.__queue_consume = queue_consume
|
|
30
|
+
self.__amqp_url = amqp_url
|
|
31
|
+
self.__credentials = pika.PlainCredentials(
|
|
32
|
+
credentials.username, credentials.password
|
|
33
|
+
)
|
|
34
|
+
self.__routes = {}
|
|
35
|
+
|
|
36
|
+
def include_router(self, router: ChatbotRouter, prefix: str):
|
|
37
|
+
if 'START' not in router.routes.keys():
|
|
38
|
+
raise ChatbotError('Erro ao incluir rota, START não encontrado!')
|
|
39
|
+
|
|
40
|
+
prefixed_routes = {
|
|
41
|
+
(
|
|
42
|
+
f'START{prefix.upper()}'
|
|
43
|
+
if key.upper() == 'START'
|
|
44
|
+
else f'START{prefix.upper()}{key.upper()}'
|
|
45
|
+
): value
|
|
46
|
+
for key, value in router.routes.items()
|
|
47
|
+
}
|
|
48
|
+
self.__routes.update(prefixed_routes)
|
|
49
|
+
|
|
50
|
+
def route(self, state: str, default_message: str):
|
|
51
|
+
def decorator(func):
|
|
52
|
+
params = dict()
|
|
53
|
+
signature = inspect.signature(func)
|
|
54
|
+
output_param = signature.return_annotation
|
|
55
|
+
|
|
56
|
+
for name, param in signature.parameters.items():
|
|
57
|
+
param_type = (
|
|
58
|
+
param.annotation
|
|
59
|
+
if param.annotation != inspect.Parameter.empty
|
|
60
|
+
else 'Any'
|
|
61
|
+
)
|
|
62
|
+
params[param_type] = name
|
|
63
|
+
debug(f'Parameter: {name}, Type: {param_type}')
|
|
64
|
+
|
|
65
|
+
if Message not in params.keys():
|
|
66
|
+
raise ChatbotError(
|
|
67
|
+
'Função não recebe Message, parâmetro obrigatório!'
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
self.__routes[state.upper()] = {
|
|
71
|
+
'function': func,
|
|
72
|
+
'default_message': default_message,
|
|
73
|
+
'params': params,
|
|
74
|
+
'return': output_param,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@wraps(func)
|
|
78
|
+
def wrapper(*args, **kwargs):
|
|
79
|
+
return func(*args, **kwargs)
|
|
80
|
+
|
|
81
|
+
return wrapper
|
|
82
|
+
|
|
83
|
+
return decorator
|
|
84
|
+
|
|
85
|
+
def start(self):
|
|
86
|
+
try: # Verificar se recursão não vai estourar
|
|
87
|
+
connection = pika.BlockingConnection(
|
|
88
|
+
pika.ConnectionParameters(
|
|
89
|
+
host=self.__amqp_url,
|
|
90
|
+
virtual_host=self.__virtual_host,
|
|
91
|
+
credentials=self.__credentials,
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
channel = connection.channel()
|
|
95
|
+
|
|
96
|
+
channel.basic_qos(prefetch_count=self.__prefetch_count)
|
|
97
|
+
channel.basic_consume(
|
|
98
|
+
queue=self.__queue_consume,
|
|
99
|
+
on_message_callback=self.on_request,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
debug('[x] Aguardando solicitações RPC')
|
|
103
|
+
channel.start_consuming()
|
|
104
|
+
except pika.exceptions.StreamLostError as e:
|
|
105
|
+
debug(e)
|
|
106
|
+
self.start()
|
|
107
|
+
|
|
108
|
+
def on_request(self, ch, method, props, body):
|
|
109
|
+
message = body.decode()
|
|
110
|
+
response = self.process_message(message)
|
|
111
|
+
|
|
112
|
+
ch.basic_publish(
|
|
113
|
+
exchange='',
|
|
114
|
+
routing_key=props.reply_to,
|
|
115
|
+
properties=pika.BasicProperties(
|
|
116
|
+
correlation_id=props.correlation_id
|
|
117
|
+
),
|
|
118
|
+
body=str(response),
|
|
119
|
+
)
|
|
120
|
+
ch.basic_ack(delivery_tag=method.delivery_tag)
|
|
121
|
+
|
|
122
|
+
def process_message(self, message: str):
|
|
123
|
+
data = json.loads(message)
|
|
124
|
+
message_imported = self.__transform_message(data)
|
|
125
|
+
|
|
126
|
+
customer_id = message_imported.customer_id
|
|
127
|
+
|
|
128
|
+
menu = self.__user_state.get_menu(customer_id)
|
|
129
|
+
handler = self.__routes.get(menu, None)
|
|
130
|
+
|
|
131
|
+
if not handler:
|
|
132
|
+
raise ChatbotMessageError(
|
|
133
|
+
customer_id, f'Rota não encontrada para {menu}!'
|
|
134
|
+
)
|
|
135
|
+
func = handler['function']
|
|
136
|
+
message_name = handler['params'].get(Message)
|
|
137
|
+
user_state_name = handler['params'].get(UserState, None)
|
|
138
|
+
|
|
139
|
+
kwargs = {message_name: message_imported}
|
|
140
|
+
if user_state_name:
|
|
141
|
+
kwargs[user_state_name] = SimpleUserState(
|
|
142
|
+
menu, list(self.__routes.keys())
|
|
143
|
+
)
|
|
144
|
+
return func(**kwargs)
|
|
145
|
+
|
|
146
|
+
def __transform_message(self, message: dict) -> Message:
|
|
147
|
+
return Message(
|
|
148
|
+
type=message.get('type', ''),
|
|
149
|
+
text=message.get('text', ''),
|
|
150
|
+
customer_id=message.get('customer_id', ''),
|
|
151
|
+
channel=message.get('channel', ''),
|
|
152
|
+
customer_phone=message.get('customer_phone', ''),
|
|
153
|
+
company_phone=message.get('company_phone', ''),
|
|
154
|
+
status=message.get('status'),
|
|
155
|
+
)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
from abc import ABC
|
|
3
|
+
from functools import wraps
|
|
4
|
+
from logging import debug
|
|
5
|
+
|
|
6
|
+
from ..error.chatbot_error import ChatbotError, ChatbotMessageError
|
|
7
|
+
from ..messages.base_message_consumer import MessageConsumer
|
|
8
|
+
from ..types.message_types import Message
|
|
9
|
+
from ..types.route import Route
|
|
10
|
+
from ..types.user_state import UserState
|
|
11
|
+
from .chatbot_router import ChatbotRouter
|
|
12
|
+
from ..types.output_state import ChatbotResponse, RedirectResponse
|
|
13
|
+
|
|
14
|
+
class ChatbotApp(ABC):
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
user_state: UserState,
|
|
18
|
+
message_consumer: MessageConsumer,
|
|
19
|
+
):
|
|
20
|
+
self.__message_consumer = message_consumer
|
|
21
|
+
self.__user_state = user_state
|
|
22
|
+
self.__routes = {}
|
|
23
|
+
|
|
24
|
+
def include_router(self, router: ChatbotRouter, prefix: str):
|
|
25
|
+
if 'START' not in router.routes.keys():
|
|
26
|
+
raise ChatbotError('Erro ao incluir rota, START não encontrado!')
|
|
27
|
+
|
|
28
|
+
prefixed_routes = {
|
|
29
|
+
(
|
|
30
|
+
f'START{prefix.upper()}'
|
|
31
|
+
if key.upper() == 'START'
|
|
32
|
+
else f'START{prefix.upper()}{key.upper().replace("START", "")}'
|
|
33
|
+
): value
|
|
34
|
+
for key, value in router.routes.items()
|
|
35
|
+
}
|
|
36
|
+
self.__routes.update(prefixed_routes)
|
|
37
|
+
|
|
38
|
+
def route(self, route_name: str):
|
|
39
|
+
if not 'START' in route_name:
|
|
40
|
+
route_name = f'START{route_name}'
|
|
41
|
+
|
|
42
|
+
def decorator(func):
|
|
43
|
+
params = dict()
|
|
44
|
+
signature = inspect.signature(func)
|
|
45
|
+
output_param = signature.return_annotation
|
|
46
|
+
|
|
47
|
+
for name, param in signature.parameters.items():
|
|
48
|
+
param_type = (
|
|
49
|
+
param.annotation
|
|
50
|
+
if param.annotation != inspect.Parameter.empty
|
|
51
|
+
else 'Any'
|
|
52
|
+
)
|
|
53
|
+
params[param_type] = name
|
|
54
|
+
debug(f'Parameter: {name}, Type: {param_type}')
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
self.__routes[route_name.strip().upper()] = {
|
|
58
|
+
'function': func,
|
|
59
|
+
'params': params,
|
|
60
|
+
'return': output_param,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@wraps(func)
|
|
64
|
+
def wrapper(*args, **kwargs):
|
|
65
|
+
return func(*args, **kwargs)
|
|
66
|
+
|
|
67
|
+
return wrapper
|
|
68
|
+
|
|
69
|
+
return decorator
|
|
70
|
+
|
|
71
|
+
def start(self):
|
|
72
|
+
self.__message_consumer.start_consume(self.process_message)
|
|
73
|
+
|
|
74
|
+
def process_message(self, message: Message):
|
|
75
|
+
customer_id = message.customer_id
|
|
76
|
+
|
|
77
|
+
menu = self.__user_state.get_menu(customer_id)
|
|
78
|
+
menu = menu.upper()
|
|
79
|
+
handler = self.__routes.get(menu, None)
|
|
80
|
+
|
|
81
|
+
if not handler:
|
|
82
|
+
raise ChatbotMessageError(
|
|
83
|
+
customer_id, f'Rota não encontrada para {menu}!'
|
|
84
|
+
)
|
|
85
|
+
func = handler['function']
|
|
86
|
+
message_name = handler['params'].get(Message, None)
|
|
87
|
+
route_state_name = handler['params'].get(Route, None)
|
|
88
|
+
|
|
89
|
+
kwargs = dict()
|
|
90
|
+
if message_name:
|
|
91
|
+
kwargs[message_name] = message
|
|
92
|
+
if route_state_name:
|
|
93
|
+
kwargs[route_state_name] = Route(menu, list(self.__routes.keys()))
|
|
94
|
+
|
|
95
|
+
message_response = func(**kwargs)
|
|
96
|
+
|
|
97
|
+
if type(message_response) in (str, float, int):
|
|
98
|
+
return message_response
|
|
99
|
+
elif type(message_response) == ChatbotResponse:
|
|
100
|
+
route = self.__adjust_route(message_response.route, menu)
|
|
101
|
+
self.__user_state.set_menu(customer_id, route)
|
|
102
|
+
return message_response.message
|
|
103
|
+
elif type(message_response) == RedirectResponse:
|
|
104
|
+
route = self.__adjust_route(message_response.route, menu)
|
|
105
|
+
self.__user_state.set_menu(customer_id, route)
|
|
106
|
+
return self.process_message(message)
|
|
107
|
+
else:
|
|
108
|
+
raise ChatbotError('Tipo de retorno inválido!')
|
|
109
|
+
|
|
110
|
+
def __adjust_route(self, route: str, absolute_route:str) -> str:
|
|
111
|
+
if not route:
|
|
112
|
+
return absolute_route
|
|
113
|
+
|
|
114
|
+
if not 'START' in route:
|
|
115
|
+
route = absolute_route+route
|
|
116
|
+
|
|
117
|
+
return route
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
from functools import wraps
|
|
3
|
+
from logging import debug
|
|
4
|
+
|
|
5
|
+
from ..error.chatbot_error import ChatbotError
|
|
6
|
+
from ..types.message_types import Message
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ChatbotRouter:
|
|
10
|
+
def __init__(self):
|
|
11
|
+
self.routes = {}
|
|
12
|
+
|
|
13
|
+
def route(self, route_name: str):
|
|
14
|
+
if not 'START' in route_name:
|
|
15
|
+
route_name = f'START{route_name}'
|
|
16
|
+
|
|
17
|
+
def decorator(func):
|
|
18
|
+
params = dict()
|
|
19
|
+
signature = inspect.signature(func)
|
|
20
|
+
output_param = signature.return_annotation
|
|
21
|
+
|
|
22
|
+
for name, param in signature.parameters.items():
|
|
23
|
+
param_type = (
|
|
24
|
+
param.annotation
|
|
25
|
+
if param.annotation != inspect.Parameter.empty
|
|
26
|
+
else 'Any'
|
|
27
|
+
)
|
|
28
|
+
params[param_type] = name
|
|
29
|
+
debug(f'Parameter: {name}, Type: {param_type}')
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
self.routes[route_name.strip().upper()] = {
|
|
33
|
+
'function': func,
|
|
34
|
+
'params': params,
|
|
35
|
+
'return': output_param,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@wraps(func)
|
|
39
|
+
def wrapper(*args, **kwargs):
|
|
40
|
+
return func(*args, **kwargs)
|
|
41
|
+
|
|
42
|
+
return wrapper
|
|
43
|
+
|
|
44
|
+
return decorator
|
|
45
|
+
|
|
46
|
+
def include_router(self, router: 'ChatbotRouter', prefix: str):
|
|
47
|
+
if 'START' not in router.routes.keys():
|
|
48
|
+
raise ChatbotError('Erro ao incluir rota, START não encontrado!')
|
|
49
|
+
|
|
50
|
+
prefixed_routes = {
|
|
51
|
+
(
|
|
52
|
+
f'{prefix.upper()}'
|
|
53
|
+
if key.upper() == 'START'
|
|
54
|
+
else f'START{prefix.upper()}{key.upper().replace("START", "")}'
|
|
55
|
+
): value
|
|
56
|
+
for key, value in router.routes.items()
|
|
57
|
+
}
|
|
58
|
+
self.routes.update(prefixed_routes)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class ChatbotError(Exception):
|
|
2
|
+
def __init__(self, message: str):
|
|
3
|
+
self.message = message
|
|
4
|
+
super().__init__(self.message)
|
|
5
|
+
|
|
6
|
+
def __str__(self):
|
|
7
|
+
return f'ChatbotError: {self.message}'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ChatbotMessageError(Exception):
|
|
11
|
+
def __init__(self, customer_id: str, message: str):
|
|
12
|
+
self.customer_id = customer_id
|
|
13
|
+
self.message = f'{message} ID recebido: {customer_id}'
|
|
14
|
+
super().__init__(self.message)
|
|
15
|
+
|
|
16
|
+
def __str__(self):
|
|
17
|
+
return f'InvalidCustomerIDError: {self.message}'
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from logging import debug, info
|
|
3
|
+
|
|
4
|
+
import pika
|
|
5
|
+
|
|
6
|
+
from ..auth.credentials import Credential
|
|
7
|
+
from ..types.message_types import Message
|
|
8
|
+
from .base_message_consumer import MessageConsumer
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RabbitMessageConsumer(MessageConsumer):
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
amqp_url: str,
|
|
15
|
+
queue_consume: str,
|
|
16
|
+
credentials: Credential,
|
|
17
|
+
prefetch_count: int = 1,
|
|
18
|
+
virtual_host: str = '/',
|
|
19
|
+
) -> None:
|
|
20
|
+
self.__virtual_host = virtual_host
|
|
21
|
+
self.__prefetch_count = prefetch_count
|
|
22
|
+
self.__queue_consume = queue_consume
|
|
23
|
+
self.__amqp_url = amqp_url
|
|
24
|
+
self.__credentials = pika.PlainCredentials(
|
|
25
|
+
credentials.username, credentials.password
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
def start_consume(self, process_message: callable):
|
|
29
|
+
try: # Verificar se recursão não vai estourar
|
|
30
|
+
connection = pika.BlockingConnection(
|
|
31
|
+
pika.ConnectionParameters(
|
|
32
|
+
host=self.__amqp_url,
|
|
33
|
+
virtual_host=self.__virtual_host,
|
|
34
|
+
credentials=self.__credentials,
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
channel = connection.channel()
|
|
38
|
+
|
|
39
|
+
channel.basic_qos(prefetch_count=self.__prefetch_count)
|
|
40
|
+
channel.basic_consume(
|
|
41
|
+
queue=self.__queue_consume,
|
|
42
|
+
on_message_callback=lambda c, m, p, b: self.on_request(
|
|
43
|
+
c, m, p, b, process_message
|
|
44
|
+
),
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
info('[x] Aguardando solicitações RPC')
|
|
48
|
+
channel.start_consuming()
|
|
49
|
+
except pika.exceptions.StreamLostError as e:
|
|
50
|
+
debug(e)
|
|
51
|
+
self.start_consume(process_message)
|
|
52
|
+
|
|
53
|
+
def on_request(self, ch, method, props, body, process_message):
|
|
54
|
+
message = body.decode()
|
|
55
|
+
message_json = json.loads(message)
|
|
56
|
+
pure_message = self.__transform_message(message_json)
|
|
57
|
+
response = process_message(pure_message)
|
|
58
|
+
|
|
59
|
+
ch.basic_publish(
|
|
60
|
+
exchange='',
|
|
61
|
+
routing_key=props.reply_to,
|
|
62
|
+
properties=pika.BasicProperties(
|
|
63
|
+
correlation_id=props.correlation_id
|
|
64
|
+
),
|
|
65
|
+
body=str(response),
|
|
66
|
+
)
|
|
67
|
+
ch.basic_ack(delivery_tag=method.delivery_tag)
|
|
68
|
+
|
|
69
|
+
def __transform_message(self, message: dict) -> Message:
|
|
70
|
+
return Message(
|
|
71
|
+
type=message.get('type', ''),
|
|
72
|
+
text=message.get('text', ''),
|
|
73
|
+
customer_id=message.get('customer_id', ''),
|
|
74
|
+
channel=message.get('channel', ''),
|
|
75
|
+
customer_phone=message.get('customer_phone', ''),
|
|
76
|
+
company_phone=message.get('company_phone', ''),
|
|
77
|
+
status=message.get('status'),
|
|
78
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
3
|
+
messageTypes = Union[str, float, int, None]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ChatbotResponse:
|
|
7
|
+
def __init__(
|
|
8
|
+
self, message: messageTypes = None, route: str = None
|
|
9
|
+
) -> None:
|
|
10
|
+
self.message = message
|
|
11
|
+
self.route = route
|
|
12
|
+
|
|
13
|
+
class RedirectResponse:
|
|
14
|
+
def __init__(self, route: str) -> None:
|
|
15
|
+
self.route = route
|
chatgraph/types/route.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from ..error.route_error import RouteError
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Route:
|
|
5
|
+
def __init__(self, current: str, routes: list[str]):
|
|
6
|
+
self.current = current
|
|
7
|
+
self.routes = routes
|
|
8
|
+
|
|
9
|
+
def get_previous(self) -> str:
|
|
10
|
+
"""
|
|
11
|
+
Retorna o caminho anterior ao caminho atual.
|
|
12
|
+
"""
|
|
13
|
+
if self.current == 'START':
|
|
14
|
+
raise RouteError('Não há caminho anterior ao START')
|
|
15
|
+
|
|
16
|
+
previous_route = '/'.join(self.current.split('/')[:-1])
|
|
17
|
+
return previous_route
|
|
18
|
+
|
|
19
|
+
def get_next(self, next_part: str) -> str:
|
|
20
|
+
"""
|
|
21
|
+
Monta e retorna o próximo caminho com base na parte fornecida.
|
|
22
|
+
"""
|
|
23
|
+
next_part = next_part.strip().upper()
|
|
24
|
+
next_route = f"{self.current.rstrip('/')}{next_part}"
|
|
25
|
+
if next_route not in self.routes:
|
|
26
|
+
raise RouteError(f'Rota não encontrada: {next_route}')
|
|
27
|
+
return next_route
|
|
28
|
+
|
|
29
|
+
def __str__(self):
|
|
30
|
+
return f'Route(current={self.current})'
|
|
31
|
+
|
|
32
|
+
def __repr__(self):
|
|
33
|
+
return self.__str__()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class UserState(ABC):
|
|
5
|
+
@abstractmethod
|
|
6
|
+
def get_menu(self, customer_id: str):
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
@abstractmethod
|
|
10
|
+
def set_menu(self, customer_id: str, menu: str):
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SimpleUserState(UserState):
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self.states = {}
|
|
17
|
+
|
|
18
|
+
def get_menu(self, customer_id: str) -> str:
|
|
19
|
+
menu = self.states.get(customer_id, 'START')
|
|
20
|
+
if menu == 'START':
|
|
21
|
+
self.set_menu(customer_id, menu)
|
|
22
|
+
return menu
|
|
23
|
+
|
|
24
|
+
def set_menu(self, customer_id: str, menu: str|None=None) -> str:
|
|
25
|
+
if menu:
|
|
26
|
+
self.states[customer_id] = menu.upper()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Irisson Lima
|
|
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 HOLDERS 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,15 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: chatgraph
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A user-friendly chatbot library
|
|
5
|
+
Author: Irisson N. Lima
|
|
6
|
+
Author-email: irisson.lima@verdecard.com.br
|
|
7
|
+
Requires-Python: >=3.12,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Requires-Dist: pika (>=1.3.2,<2.0.0)
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# chatgraph
|
|
14
|
+
ChatGraph is a user-friendly chatbot library that offers seamless integration with RabbitMQ. It is designed to simplify the development process by providing intuitive interfaces and robust messaging capabilities, making it easy to build and deploy chatbots leveraging RabbitMQ's powerful messaging infrastructure.
|
|
15
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
chatgraph/__init__.py,sha256=qPA9Vw2SZivqVUlB19D-YrRuequMThR6kgO2XsZcwDc,602
|
|
2
|
+
chatgraph/auth/credentials.py,sha256=tc8FvjWu-cFdx9ChzxkSDXX04g3eSPE6FcsRW0xg40k,917
|
|
3
|
+
chatgraph/bot/chatbot_app.py,sha256=Uix25rT8gCoCKYziSW0WrBsQzs4DU_L0GSHwdgu4et0,5263
|
|
4
|
+
chatgraph/bot/chatbot_model.py,sha256=QqnCZvEOfp--zn-mnEXL3p5pqxPN_ztnhXdfNEeDnHk,4102
|
|
5
|
+
chatgraph/bot/chatbot_router.py,sha256=48AiRaXtlCtad2imuUA-uK_ppxUwa6GEkPv8YXTB29g,1836
|
|
6
|
+
chatgraph/error/chatbot_error.py,sha256=rTBPDPB2CNoZDKfL5PgWtj36jdoepe_gO6Opb5kd3mA,545
|
|
7
|
+
chatgraph/error/route_error.py,sha256=jEi621zHJbB3LnwvbtP_o9u0AkoddrHoGo1OKAPLMWk,213
|
|
8
|
+
chatgraph/messages/__init__.py,sha256=lKpQI7FXjSJuwcV9ItX6ZJditEm6kQpGSPoTuOtMHy4,94
|
|
9
|
+
chatgraph/messages/base_message_consumer.py,sha256=hpoLLqxk5mQXRnS3s_lkOPosgwfMmvxAOftkt8P0Qvc,203
|
|
10
|
+
chatgraph/messages/rabbitMQ_message_consumer.py,sha256=z4RQ21N1aUwx9OOrkQRd04fSTHfw3uzPsOlKyLGKO5c,2753
|
|
11
|
+
chatgraph/types/message_types.py,sha256=pTUIGOiNdoYg7i8zNXRdGSg4z86bJZWjYbn3cII0eI0,249
|
|
12
|
+
chatgraph/types/output_state.py,sha256=cBdZXnsyEVwmxno5De_vHUwh9cPokPvFpeiLM_3VkZQ,357
|
|
13
|
+
chatgraph/types/route.py,sha256=818fovzJZyjPl27ZAvCZpZNUCccyO_6ciyLJxOHI1JI,1034
|
|
14
|
+
chatgraph/types/user_state.py,sha256=3kAyAUue7KTi-Rvf-cYDzX3L-PMa4POmRFgAUvS9fTo,669
|
|
15
|
+
chatgraph-0.1.0.dist-info/LICENSE,sha256=rVJozpRzDlplOpvI8A1GvmfVS0ReYdZvMWc1j2jV0v8,1090
|
|
16
|
+
chatgraph-0.1.0.dist-info/METADATA,sha256=VNDKsd2e0HPI_RN6tuFUEvKBKyeJgokmJujlqw5z74k,695
|
|
17
|
+
chatgraph-0.1.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
18
|
+
chatgraph-0.1.0.dist-info/RECORD,,
|