kobil-chat 1.2.0__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.
- kobil_chat-1.2.0/PKG-INFO +44 -0
- kobil_chat-1.2.0/README.md +33 -0
- kobil_chat-1.2.0/kobil/chat/__init__.py +197 -0
- kobil_chat-1.2.0/kobil_chat.egg-info/PKG-INFO +44 -0
- kobil_chat-1.2.0/kobil_chat.egg-info/SOURCES.txt +8 -0
- kobil_chat-1.2.0/kobil_chat.egg-info/dependency_links.txt +1 -0
- kobil_chat-1.2.0/kobil_chat.egg-info/requires.txt +1 -0
- kobil_chat-1.2.0/kobil_chat.egg-info/top_level.txt +1 -0
- kobil_chat-1.2.0/pyproject.toml +20 -0
- kobil_chat-1.2.0/setup.cfg +4 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kobil-chat
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: KOBIL Chat (Mercury) client for Python with automatic OAuth token management.
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Repository, https://github.com/uveysservetoglu/kobil-sdks
|
|
7
|
+
Keywords: kobil,chat,mercury,messaging
|
|
8
|
+
Requires-Python: >=3.8
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: kobil-core>=1.0
|
|
11
|
+
|
|
12
|
+
# kobil-chat
|
|
13
|
+
|
|
14
|
+
KOBIL Chat (Mercury) client for Python, with automatic OAuth token management.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install kobil-chat
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from kobil.chat import ChatClient
|
|
22
|
+
|
|
23
|
+
chat = ChatClient(
|
|
24
|
+
chat_base_url="https://idp.cloud.kobil.com/auth/realms/{TENANT}/mpower/v1/users/{USER_ID}",
|
|
25
|
+
tenant_name="acme",
|
|
26
|
+
token_auth_mode="basic", # chat token endpoint may require Basic auth
|
|
27
|
+
media_download_url="https://idp.cloud.kobil.com/v1/mpower/tenants/{TENANT}/media/{MEDIA_ID}/download",
|
|
28
|
+
# credentials optional if KOBIL_* env vars are set
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
chat.send_text(user_id, "Hi!", title="Update")
|
|
32
|
+
chat.send_choice(user_id, "Continue?", ["Yes", "No"])
|
|
33
|
+
chat.send_smart_screen(user_id, "Opening…", "service_client_id")
|
|
34
|
+
chat.send_attachment(user_id, open("a.pdf", "rb").read(), file_name="a.pdf", text="Please review")
|
|
35
|
+
data = chat.get_attachment_file(media_id) # bytes
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`{TENANT}` is filled from `tenant_name`, `{USER_ID}` from the call, `{MEDIA_ID}` from
|
|
39
|
+
`get_attachment_file`. Send operations return `{"messageId": ..., "instanceId": ...}`.
|
|
40
|
+
Failures raise `kobil.chat.KobilChatError`.
|
|
41
|
+
|
|
42
|
+
## License
|
|
43
|
+
|
|
44
|
+
MIT
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# kobil-chat
|
|
2
|
+
|
|
3
|
+
KOBIL Chat (Mercury) client for Python, with automatic OAuth token management.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install kobil-chat
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from kobil.chat import ChatClient
|
|
11
|
+
|
|
12
|
+
chat = ChatClient(
|
|
13
|
+
chat_base_url="https://idp.cloud.kobil.com/auth/realms/{TENANT}/mpower/v1/users/{USER_ID}",
|
|
14
|
+
tenant_name="acme",
|
|
15
|
+
token_auth_mode="basic", # chat token endpoint may require Basic auth
|
|
16
|
+
media_download_url="https://idp.cloud.kobil.com/v1/mpower/tenants/{TENANT}/media/{MEDIA_ID}/download",
|
|
17
|
+
# credentials optional if KOBIL_* env vars are set
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
chat.send_text(user_id, "Hi!", title="Update")
|
|
21
|
+
chat.send_choice(user_id, "Continue?", ["Yes", "No"])
|
|
22
|
+
chat.send_smart_screen(user_id, "Opening…", "service_client_id")
|
|
23
|
+
chat.send_attachment(user_id, open("a.pdf", "rb").read(), file_name="a.pdf", text="Please review")
|
|
24
|
+
data = chat.get_attachment_file(media_id) # bytes
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`{TENANT}` is filled from `tenant_name`, `{USER_ID}` from the call, `{MEDIA_ID}` from
|
|
28
|
+
`get_attachment_file`. Send operations return `{"messageId": ..., "instanceId": ...}`.
|
|
29
|
+
Failures raise `kobil.chat.KobilChatError`.
|
|
30
|
+
|
|
31
|
+
## License
|
|
32
|
+
|
|
33
|
+
MIT
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"""kobil.chat — KOBIL Chat (Mercury) client."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import urllib.parse
|
|
8
|
+
from typing import Any, Dict, List, Optional, Sequence
|
|
9
|
+
|
|
10
|
+
from kobil.core import (
|
|
11
|
+
DefaultTokenProvider,
|
|
12
|
+
KobilError,
|
|
13
|
+
ServiceClient,
|
|
14
|
+
TokenProvider,
|
|
15
|
+
credentials_from_env,
|
|
16
|
+
new_uuid,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = ["ChatClient", "KobilChatError"]
|
|
20
|
+
|
|
21
|
+
_MIME_BY_EXT = {
|
|
22
|
+
".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif",
|
|
23
|
+
".bmp": "image/bmp", ".webp": "image/webp", ".svg": "image/svg+xml", ".pdf": "application/pdf",
|
|
24
|
+
".doc": "application/msword",
|
|
25
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
26
|
+
".xls": "application/vnd.ms-excel",
|
|
27
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
28
|
+
".ppt": "application/vnd.ms-powerpoint",
|
|
29
|
+
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
30
|
+
".txt": "text/plain", ".csv": "text/csv", ".json": "application/json", ".xml": "application/xml",
|
|
31
|
+
".zip": "application/zip", ".rar": "application/vnd.rar", ".7z": "application/x-7z-compressed",
|
|
32
|
+
".mp3": "audio/mpeg", ".mp4": "video/mp4", ".avi": "video/x-msvideo", ".mov": "video/quicktime",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class KobilChatError(KobilError):
|
|
37
|
+
"""Thrown when a chat operation fails."""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ChatClient(ServiceClient):
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
*,
|
|
44
|
+
chat_base_url: str,
|
|
45
|
+
token_endpoint: Optional[str] = None,
|
|
46
|
+
client_id: Optional[str] = None,
|
|
47
|
+
client_secret: Optional[str] = None,
|
|
48
|
+
token_auth_mode: Optional[str] = None,
|
|
49
|
+
media_download_url: Optional[str] = None,
|
|
50
|
+
tenant_name: Optional[str] = None,
|
|
51
|
+
service_uuid: Optional[str] = None,
|
|
52
|
+
version: int = 3,
|
|
53
|
+
escape_attachment_unicode: bool = True,
|
|
54
|
+
token_provider: Optional[TokenProvider] = None,
|
|
55
|
+
) -> None:
|
|
56
|
+
credentials = credentials_from_env(
|
|
57
|
+
token_endpoint=token_endpoint,
|
|
58
|
+
client_id=client_id,
|
|
59
|
+
client_secret=client_secret,
|
|
60
|
+
token_auth_mode=token_auth_mode,
|
|
61
|
+
)
|
|
62
|
+
super().__init__(token_provider or DefaultTokenProvider(credentials))
|
|
63
|
+
if not chat_base_url:
|
|
64
|
+
raise KobilChatError("chat_base_url is required.")
|
|
65
|
+
self._chat_base_url = chat_base_url
|
|
66
|
+
self._media_download_url = media_download_url
|
|
67
|
+
self._tenant_name = tenant_name
|
|
68
|
+
self._client_id = credentials.client_id
|
|
69
|
+
self._service_uuid = service_uuid
|
|
70
|
+
self._version = version
|
|
71
|
+
self._escape_unicode = escape_attachment_unicode
|
|
72
|
+
|
|
73
|
+
def _make_error(self, message, status_code=None, response_body=None, cause=None) -> KobilError:
|
|
74
|
+
return KobilChatError(message, status_code, response_body, cause)
|
|
75
|
+
|
|
76
|
+
def send_text(self, user_id: str, text: str, title: Optional[str] = None) -> Dict[str, Any]:
|
|
77
|
+
self._require_user(user_id)
|
|
78
|
+
return self.send_json("POST", self._message_url(user_id), {
|
|
79
|
+
"serviceUuid": self._resolve_service_uuid(),
|
|
80
|
+
"messageType": "processChatMessage",
|
|
81
|
+
"version": self._version,
|
|
82
|
+
"messageContent": {"messageText": self._compose_text(title, text)},
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
def send_choice(self, user_id: str, text: str, choices: Sequence[str]) -> Dict[str, Any]:
|
|
86
|
+
self._require_user(user_id)
|
|
87
|
+
if not choices:
|
|
88
|
+
raise KobilChatError("At least one choice is required.")
|
|
89
|
+
return self.send_json("POST", self._message_url(user_id), {
|
|
90
|
+
"serviceUuid": self._resolve_service_uuid(),
|
|
91
|
+
"messageType": "choiceRequest",
|
|
92
|
+
"version": self._version,
|
|
93
|
+
"messageContent": {"messageText": text, "choices": [{"text": c} for c in choices]},
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
def send_smart_screen(self, user_id: str, text: str, smart_screen_service_uuid: str) -> Dict[str, Any]:
|
|
97
|
+
self._require_user(user_id)
|
|
98
|
+
if not smart_screen_service_uuid:
|
|
99
|
+
raise KobilChatError("smart_screen_service_uuid cannot be empty.")
|
|
100
|
+
return self.send_json("POST", self._message_url(user_id), {
|
|
101
|
+
"serviceUuid": self._resolve_service_uuid(),
|
|
102
|
+
"messageType": "smartScreenService",
|
|
103
|
+
"version": self._version,
|
|
104
|
+
"messageContent": {"messageText": text, "smartScreenServiceUuid": smart_screen_service_uuid},
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
def send_attachment(self, user_id: str, file: bytes, file_name: Optional[str] = None,
|
|
108
|
+
content_type: Optional[str] = None, file_extension: Optional[str] = None,
|
|
109
|
+
title: Optional[str] = None, text: Optional[str] = None) -> Dict[str, Any]:
|
|
110
|
+
self._require_user(user_id)
|
|
111
|
+
if not file:
|
|
112
|
+
raise KobilChatError("file cannot be empty.")
|
|
113
|
+
message = {
|
|
114
|
+
"serviceUuid": self._resolve_service_uuid(),
|
|
115
|
+
"messageType": "attachmentMessage",
|
|
116
|
+
"version": self._version,
|
|
117
|
+
"messageContent": {"messageText": self._compose_attachment_text(title, text)},
|
|
118
|
+
}
|
|
119
|
+
message_json = json.dumps(message, ensure_ascii=False)
|
|
120
|
+
if self._escape_unicode:
|
|
121
|
+
message_json = _escape_non_ascii(message_json)
|
|
122
|
+
files = {
|
|
123
|
+
"attachment": {
|
|
124
|
+
"content": file,
|
|
125
|
+
"filename": _file_name(file_name, file_extension),
|
|
126
|
+
"content_type": _content_type(content_type, file_name, file_extension),
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return self.send_multipart("POST", self._media_url(user_id), {"message": message_json}, files)
|
|
130
|
+
|
|
131
|
+
def get_attachment_file(self, media_id: str) -> bytes:
|
|
132
|
+
if not media_id:
|
|
133
|
+
raise KobilChatError("media_id cannot be empty.")
|
|
134
|
+
if not self._media_download_url:
|
|
135
|
+
raise KobilChatError("media_download_url is required to download attachments.")
|
|
136
|
+
url = self._replace_tokens(self._media_download_url, {"{TENANT}": self._tenant_name, "{MEDIA_ID}": media_id})
|
|
137
|
+
return self.send_for_bytes("POST", url)
|
|
138
|
+
|
|
139
|
+
def _message_url(self, user_id: str) -> str:
|
|
140
|
+
return self._user_base_url(user_id) + "/message"
|
|
141
|
+
|
|
142
|
+
def _media_url(self, user_id: str) -> str:
|
|
143
|
+
return self._user_base_url(user_id) + "/media"
|
|
144
|
+
|
|
145
|
+
def _user_base_url(self, user_id: str) -> str:
|
|
146
|
+
return self._replace_tokens(self._chat_base_url, {"{TENANT}": self._tenant_name, "{USER_ID}": user_id}).rstrip("/")
|
|
147
|
+
|
|
148
|
+
def _resolve_service_uuid(self) -> str:
|
|
149
|
+
return self._service_uuid or self._client_id
|
|
150
|
+
|
|
151
|
+
@staticmethod
|
|
152
|
+
def _compose_text(title: Optional[str], text: str) -> str:
|
|
153
|
+
return f"{title}\n\n{text}" if title else text
|
|
154
|
+
|
|
155
|
+
@staticmethod
|
|
156
|
+
def _compose_attachment_text(title: Optional[str], text: Optional[str]) -> str:
|
|
157
|
+
out = ""
|
|
158
|
+
if title:
|
|
159
|
+
out += f"*{title}*"
|
|
160
|
+
if title and text:
|
|
161
|
+
out += f"\n\n{text}"
|
|
162
|
+
elif not title and text:
|
|
163
|
+
out += text
|
|
164
|
+
return out
|
|
165
|
+
|
|
166
|
+
@staticmethod
|
|
167
|
+
def _replace_tokens(template: str, tokens: Dict[str, Optional[str]]) -> str:
|
|
168
|
+
for token, value in tokens.items():
|
|
169
|
+
template = template.replace(token, urllib.parse.quote(value or "", safe=""))
|
|
170
|
+
return template
|
|
171
|
+
|
|
172
|
+
@staticmethod
|
|
173
|
+
def _require_user(user_id: str) -> None:
|
|
174
|
+
if not user_id:
|
|
175
|
+
raise KobilChatError("user_id cannot be empty.")
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _content_type(content_type: Optional[str], file_name: Optional[str], file_extension: Optional[str]) -> str:
|
|
179
|
+
if content_type:
|
|
180
|
+
return content_type
|
|
181
|
+
ext = (file_extension or "").lower()
|
|
182
|
+
if not ext and file_name:
|
|
183
|
+
ext = os.path.splitext(file_name)[1].lower()
|
|
184
|
+
return _MIME_BY_EXT.get(ext, "application/octet-stream")
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _file_name(file_name: Optional[str], file_extension: Optional[str]) -> str:
|
|
188
|
+
if file_name:
|
|
189
|
+
return file_name
|
|
190
|
+
ext = (file_extension or ".bin").lower()
|
|
191
|
+
if not ext.startswith("."):
|
|
192
|
+
ext = "." + ext
|
|
193
|
+
return f"{new_uuid()}{ext}"
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _escape_non_ascii(text: str) -> str:
|
|
197
|
+
return "".join(c if ord(c) < 128 else "\\u%04x" % ord(c) for c in text)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kobil-chat
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: KOBIL Chat (Mercury) client for Python with automatic OAuth token management.
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Repository, https://github.com/uveysservetoglu/kobil-sdks
|
|
7
|
+
Keywords: kobil,chat,mercury,messaging
|
|
8
|
+
Requires-Python: >=3.8
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: kobil-core>=1.0
|
|
11
|
+
|
|
12
|
+
# kobil-chat
|
|
13
|
+
|
|
14
|
+
KOBIL Chat (Mercury) client for Python, with automatic OAuth token management.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install kobil-chat
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from kobil.chat import ChatClient
|
|
22
|
+
|
|
23
|
+
chat = ChatClient(
|
|
24
|
+
chat_base_url="https://idp.cloud.kobil.com/auth/realms/{TENANT}/mpower/v1/users/{USER_ID}",
|
|
25
|
+
tenant_name="acme",
|
|
26
|
+
token_auth_mode="basic", # chat token endpoint may require Basic auth
|
|
27
|
+
media_download_url="https://idp.cloud.kobil.com/v1/mpower/tenants/{TENANT}/media/{MEDIA_ID}/download",
|
|
28
|
+
# credentials optional if KOBIL_* env vars are set
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
chat.send_text(user_id, "Hi!", title="Update")
|
|
32
|
+
chat.send_choice(user_id, "Continue?", ["Yes", "No"])
|
|
33
|
+
chat.send_smart_screen(user_id, "Opening…", "service_client_id")
|
|
34
|
+
chat.send_attachment(user_id, open("a.pdf", "rb").read(), file_name="a.pdf", text="Please review")
|
|
35
|
+
data = chat.get_attachment_file(media_id) # bytes
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`{TENANT}` is filled from `tenant_name`, `{USER_ID}` from the call, `{MEDIA_ID}` from
|
|
39
|
+
`get_attachment_file`. Send operations return `{"messageId": ..., "instanceId": ...}`.
|
|
40
|
+
Failures raise `kobil.chat.KobilChatError`.
|
|
41
|
+
|
|
42
|
+
## License
|
|
43
|
+
|
|
44
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
kobil-core>=1.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
kobil
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "kobil-chat"
|
|
7
|
+
version = "1.2.0"
|
|
8
|
+
description = "KOBIL Chat (Mercury) client for Python with automatic OAuth token management."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.8"
|
|
12
|
+
keywords = ["kobil", "chat", "mercury", "messaging"]
|
|
13
|
+
dependencies = ["kobil-core>=1.0"]
|
|
14
|
+
|
|
15
|
+
[project.urls]
|
|
16
|
+
Repository = "https://github.com/uveysservetoglu/kobil-sdks"
|
|
17
|
+
|
|
18
|
+
[tool.setuptools.packages.find]
|
|
19
|
+
include = ["kobil*"]
|
|
20
|
+
namespaces = true
|