Skip to content

Commit b77fdd9

Browse files
Merge pull request #15 from Palbahngmiyine/main
SOLAPI Python 5.0.2
2 parents aa9bc34 + 8602eab commit b77fdd9

File tree

14 files changed

+182
-11
lines changed

14 files changed

+182
-11
lines changed

examples/simple/send_kakao_bms.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from solapi import SolapiMessageService
2+
from solapi.model import Bms, KakaoOption, RequestMessage
3+
4+
# API 키와 API Secret을 설정합니다
5+
message_service = SolapiMessageService(
6+
api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET"
7+
)
8+
9+
# 카카오 알림톡 발송을 위한 옵션을 생성합니다.
10+
kakao_option = KakaoOption(
11+
pf_id="계정에 등록된 카카오 비즈니스 채널ID",
12+
template_id="계정에 등록된 카카오 브랜드 메시지 템플릿 ID",
13+
# 만약에 템플릿에 변수가 있다면 아래와 같이 설정합니다.
14+
# 값은 반드시 문자열로 넣어주셔야 합니다!
15+
# variables={
16+
# "#{name}": "홍길동",
17+
# "#{age}": "30"
18+
# }
19+
# 브랜드 메시지 발송 대상자 설정, M, N 타입은 카카오측의 별도 인허가를 받은 대상만 사용할 수 있습니다.
20+
# M: 마케팅 수신 동의 대상자 및 카카오 채널 친구
21+
# N: 마케팅 수신 동의 대상자 및 카카오 채널 친구는 제외한 대상자
22+
# I: 카카오 채널 친구
23+
bms=Bms(targeting="M"),
24+
)
25+
26+
# 단일 메시지를 생성합니다
27+
message = RequestMessage(
28+
from_="발신번호", # 발신번호 (등록된 발신번호만 사용 가능)
29+
to="수신번호", # 수신번호
30+
kakao_options=kakao_option,
31+
)
32+
33+
# 메시지를 발송합니다
34+
try:
35+
response = message_service.send(message)
36+
print("메시지 발송 성공!")
37+
print(f"Group ID: {response.group_info.group_id}")
38+
print(f"요청한 메시지 개수: {response.group_info.count.total}")
39+
print(f"성공한 메시지 개수: {response.group_info.count.registered}")
40+
except Exception as e:
41+
print(f"메시지 발송 실패: {str(e)}")
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from solapi import SolapiMessageService
2+
from solapi.model import RequestMessage, VoiceOption
3+
4+
# API 키와 API Secret을 설정합니다
5+
message_service = SolapiMessageService(
6+
api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET"
7+
)
8+
9+
"""
10+
단일 메시지를 생성합니다
11+
header_message를 사용하는 경우, 반드시 아무 버튼이나 눌러야 text 메시지가 재생됩니다.
12+
text 메시지가 재생된 이후, reply_range에 명시된 번호(1~9) 혹은 counselor_number에 값이 있을 경우 0번을 눌러야 tail_message가 재생됩니다.
13+
자세한 사항은 아래 링크를 참고해주세요!
14+
15+
https://developers.solapi.com/references/voice
16+
"""
17+
message = RequestMessage(
18+
from_="발신번호", # 발신번호 (등록된 발신번호만 사용 가능)
19+
to="수신번호", # 수신번호
20+
text="안녕하세요! SOLAPI Python SDK를 사용한 음성 메시지 발송 예제입니다.",
21+
voice_options=VoiceOption(
22+
voice_type="FEMALE",
23+
header_message="안녕하세요!",
24+
tail_message="안녕하세요!",
25+
reply_range=1,
26+
),
27+
)
28+
29+
# 메시지를 발송합니다
30+
try:
31+
response = message_service.send(message)
32+
print("메시지 발송 성공!")
33+
print(f"Group ID: {response.group_info.group_id}")
34+
print(f"요청한 메시지 개수: {response.group_info.count.total}")
35+
print(f"성공한 메시지 개수: {response.group_info.count.registered_success}")
36+
print(f"실패한 메시지 개수: {response.group_info.count.registered_failed}")
37+
except Exception as e:
38+
print(f"메시지 발송 실패: {str(e)}")

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "solapi"
3-
version = "5.0.1"
3+
version = "5.0.2"
44
description = "SOLAPI SDK for Python"
55
authors = [
66
{ name = "SOLAPI Team", email = "contact@solapi.com" }

solapi/lib/fetcher.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,17 @@ def default_fetcher(
3939
headers = {
4040
"Authorization": authorization_header_data,
4141
"Content-Type": "application/json",
42+
"Connection": "keep-alive",
4243
}
4344

44-
with httpx.Client() as client:
45+
transport = httpx.HTTPTransport(retries=3)
46+
47+
with httpx.Client(transport=transport) as client:
4548
response: Response = client.request(
46-
method=request["method"], url=request["url"], headers=headers, json=data
49+
method=request["method"],
50+
url=request["url"],
51+
headers=headers,
52+
json=data,
4753
)
4854

4955
# 4xx 에러 처리: 클라이언트 오류일 경우

solapi/lib/string_date_transfer.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,14 @@ def format_iso(date: datetime) -> str:
2727
"""
2828
utc_offset_sec = time.altzone if time.localtime().tm_isdst else time.timezone
2929
utc_offset = timedelta(seconds=-utc_offset_sec)
30-
return date.replace(tzinfo=timezone(offset=utc_offset)).isoformat()
30+
local_tz = timezone(offset=utc_offset)
31+
32+
if date.tzinfo is None:
33+
date = date.replace(tzinfo=local_tz)
34+
else:
35+
date = date.astimezone(local_tz)
36+
37+
return date.isoformat()
3138

3239

3340
def parse_iso(date_string: str) -> datetime:

solapi/model/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
from .kakao.kakao_option import KakaoOption
22
from .message_type import MessageType
33
from .request.groups.get_groups import GetGroupsRequest
4+
from .request.kakao.bms import Bms
45
from .request.message import Message as RequestMessage
56
from .request.send_message_request import SendRequestConfig
7+
from .request.voice.voice_option import VoiceOption
68
from .response.message import Message as ResponseMessage
79

810
__all__ = [
@@ -12,4 +14,6 @@
1214
"KakaoOption",
1315
"GetGroupsRequest",
1416
"MessageType",
17+
"Bms",
18+
"VoiceOption",
1519
]

solapi/model/kakao/kakao_option.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,30 @@
44
from pydantic import BaseModel, ConfigDict, field_validator
55
from pydantic.alias_generators import to_camel
66

7+
from solapi.model.request.kakao.bms import Bms
8+
79

810
class KakaoOption(BaseModel):
911
pf_id: Optional[str] = None
1012
template_id: Optional[str] = None
1113
variables: Optional[dict[str, str]] = None
1214
disable_sms: bool = False
1315
image_id: Optional[str] = None
16+
bms: Optional[Bms] = None
1417

1518
model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
1619

1720
@field_validator("variables", mode="before")
1821
@classmethod
1922
def stringify_values(cls, v: Mapping[str, object]):
2023
if isinstance(v, Mapping):
21-
# 모든 value를 str로 캐스팅
22-
return {k: str(val) for k, val in v.items()}
24+
# 키값을 #{변수명} 형태로 변환하고 모든 value를 str로 캐스팅
25+
processed_dict = {}
26+
for k, val in v.items():
27+
# 키가 이미 #{변수명} 형태가 아니면 자동으로 감싸기
28+
if not (k.startswith("#{") and k.endswith("}")):
29+
processed_key = f"#{{{k}}}"
30+
else:
31+
processed_key = k
32+
processed_dict[processed_key] = str(val)
33+
return processed_dict

solapi/model/request/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# NOTE: Python SDK가 업데이트 될 때마다 Version도 갱신해야 함!
2-
VERSION = "python/5.0.1"
2+
VERSION = "python/5.0.2"

solapi/model/request/groups/get_groups.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class GetGroupsRequest(BaseModel):
1616
model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
1717

1818

19-
class GetGroupsCrteriaType(str, Enum):
19+
class GetGroupsCriteriaType(str, Enum):
2020
group_id = "groupId"
2121
date_created = "dateCreated"
2222
scheduled_date = "scheduledDate"

solapi/model/request/kakao/bms.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from typing import Literal
2+
3+
from pydantic import BaseModel, ConfigDict
4+
5+
6+
class Bms(BaseModel):
7+
targeting: Literal["M", "N", "I"]
8+
9+
model_config = ConfigDict(
10+
populate_by_name=True,
11+
extra="ignore",
12+
)

0 commit comments

Comments
 (0)