kobil-chat 3.0.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.
@@ -0,0 +1,44 @@
1
+ Metadata-Version: 2.4
2
+ Name: kobil-chat
3
+ Version: 3.0.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: 3.0.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,8 @@
1
+ README.md
2
+ pyproject.toml
3
+ kobil/chat/__init__.py
4
+ kobil_chat.egg-info/PKG-INFO
5
+ kobil_chat.egg-info/SOURCES.txt
6
+ kobil_chat.egg-info/dependency_links.txt
7
+ kobil_chat.egg-info/requires.txt
8
+ kobil_chat.egg-info/top_level.txt
@@ -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 = "3.0.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
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+