18 releases
0.3.7 | Apr 7, 2024 |
---|---|
0.3.6 | May 10, 2023 |
0.3.4 | Aug 3, 2022 |
0.3.3 | Jun 12, 2022 |
0.2.2 | Jul 31, 2021 |
#370 in Web programming
879 downloads per month
57KB
980 lines
소개
Rust언어로 카카오 챗봇 서버를 만들 때 좀 더 쉽게 JSON 메시지 응답을 만들 수 있게 도와줍니다.
SimpleText, SimpleImage, ListCard, Carousel, BasicCard, CommerceCard, ItemCard, TextCard
JSON 데이터를 쉽게 만들 수 있도록 도와줍니다.
설치
[dependencies]
kakao-rs = "0.3"
응답 타입별 아이템
Button::share (공유 버튼), Button::link (링크 버튼), Button::text (일반 메시지만), Button::call(전화 버튼)
Items: ListItem
사용법
카카오 JSON 데이터 Bind
예제) 유저 발화문 얻기: json.userRequest.utterance
#[post("/end", format = "json", data = "<kakao>")] // rocket-rs
pub fn test(kakao: Json<Value>) -> String {
println!("{}", kakao["userRequest"]["utterance"].as_str().unwrap()); // 발화문
unimplemented!()
}
#[post("/end")]
pub async fn test(kakao: web::Json<Value>) -> impl Responder { // actix-rs
println!("{}", kakao["userRequest"]["utterance"].as_str().unwrap()); // 발화문
unimplemented!()
}
ListCard 예제
extern crate kakao_rs;
use kakao_rs::prelude::*;
fn main() {
let mut result = Template::new();
// 빠른 응답
result.add_qr(QuickReply::new("오늘", "오늘 공지 보여줘"));
result.add_qr(QuickReply::new("어제", "어제 공지 보여줘"));
let list_card = ListCard::new("리스트 카드 제목!") // 제목
.add_button(Button::text("그냥 텍스트 버튼")) // 메시지 버튼
.add_button(Button::link("link label", "https://google.com")) // 링크 버튼
.add_button(Button::share("share label").set_msg("카톡에 보이는 메시지")) // 공유 버튼, 기본적으로 message_text는 없음
.add_button(Button::call("call label", "010-1234-5679")) // 전화 버튼
.add_item(
ListItem::new("title")
.set_desc("description") // 설명
.set_link("https://naver.com"),
);
result.add_output(list_card.build()); // moved list_card's ownership
println!(
"Result: {}",
serde_json::to_string_pretty(&result).expect("Failed")
);
}
/*
Result: {
"template": {
"outputs": [
{
"listCard": {
"buttons": [
{
"label": "그냥 텍스트 버튼",
"action": "message"
},
{
"label": "link label",
"action": "webLink",
"webLinkUrl": "https://google.com"
},
{
"label": "share label",
"action": "share",
"messageText": "카톡에 보이는 메시지"
},
{
"label": "call label",
"action": "phone",
"phoneNumber": "010-1234-5679"
}
],
"header": {
"title": "리스트 카드 제목!"
},
"items": [
{
"title": "title",
"description": "description",
"link": {
"web": "https://naver.com"
}
}
]
}
}
],
"quickReplies": [
{
"action": "message",
"label": "오늘",
"messageText": "오늘 공지 보여줘"
},
{
"action": "message",
"label": "어제",
"messageText": "어제 공지 보여줘"
}
]
},
"version": "2.0"
}
*/
다른 예제
Carousel에 Card를 추가할 때는 build_card()로 카드를 빌드하세요.
Carousel 에 추가할 수 있는 카드는 다음과 같습니다.
Array<TextCard>, Array<BasicCard>, Array<CommerceCard>, Array<ListCard>, Array<itemCard>
"한 종류의 카드만 담아서 Carousel 을 만들어야 합니다. (여러 종류 같이 카카오에서 지원 안 함)"
자세한 사용법은 tests 폴더를 참고하세요.
use kakao_rs::prelude::*;
// 따로 import
// use kakao_rs::components::basics::*;
// use kakao_rs::components::buttons::*;
// use kakao_rs::components::cards::*;
use std::matches;
#[test]
fn simple_text_test() {
let mut result = Template::new();
result.add_qr(QuickReply::new(
"빠른 응답",
"빠른 응답 ㅋㅋ",
));
let simple_text = SimpleText::new("심플 텍스트 테스트");
result.add_output(simple_text.build());
let serialized = r#"{"template":{"outputs":[{"simpleText":{"text":"심플 텍스트 테스트"}}],"quickReplies":[{"action":"message","label":"빠른 응답","messageText":"빠른 응답 ㅋㅋ"}]},"version":"2.0"}"#;
assert_eq!(serialized, result.to_string());
}
// 아래처럼 Carousel에 여러 타입의 카드를 넣지 마시오.
// 카카오에서 지원하지 않습니다.
// 마지막에 추가된 카드 타입으로 json을 만듭니다.
#[test]
fn carousel_all_cards_test() {
let mut result = Template::new();
result.add_qr(QuickReply::new("빠른 응답", "빠른 응답 ㅋㅋ"));
let mut carousel = Carousel::new();
carousel.set_header("오늘 공지 n개", "n개를 더 불러왔습니다!", "https://");
let basic_card = BasicCard::new()
.set_title("1번")
.set_thumbnail("http://k.kakaocdn.net/dn/APR96/btqqH7zLanY/kD5mIPX7TdD2NAxgP29cC0/1x1.jpg");
let text_card = TextCard::new()
.set_title("챗봇 관리자센터에 오신 것을 환영합니다.")
.set_description("챗봇 관리자센터로 챗봇을 제작해 보세요.")
.add_button(
Button::new(ButtonType::Link)
.set_label("View Boarding Pass")
.set_link("https://namu.wiki/w/%EB%82%98%EC%97%B0(TWICE)"),
);
let item_card = ItemCard::new()
.set_title("title")
.set_desc("desc")
.set_thumbnail("http://dev-mk.kakao.com/dn/bot/scripts/with_barcode_blue_1x1.png")
.set_thumbnail_width(800)
.set_thumbnail_height(800)
.set_image_title("DOFQTK")
.set_image_desc("Boarding Number")
.set_item_list_alignment("right")
.set_item_list_summary("total", "$4,032.54")
.add_button(
Button::new(ButtonType::Link)
.set_label("View Boarding Pass")
.set_link("https://namu.wiki/w/%EB%82%98%EC%97%B0(TWICE)"),
)
.set_button_layout("vertical");
let commerce_card = CommerceCard::new()
.set_desc("커머스 카드")
.set_price(15000)
.set_thumbnail("http://dev-mk.kakao.com/dn/bot/scripts/with_barcode_blue_1x1.png");
carousel.add_card(text_card.build_card());
carousel.add_card(basic_card.build_card());
carousel.add_card(item_card.build_card());
carousel.add_card(commerce_card.build_card());
result.add_output(carousel.build());
let serialized = r#"{"template":{"outputs":[{"carousel":{"type":"listCard","items":[{"title":"챗봇 관리자센터에 오신 것을 환영합니다.","description":"챗봇 관리자센터로 챗봇을 제작해 보세요.","buttons":[{"label":"View Boarding Pass","action":"webLink","webLinkUrl":"https://namu.wiki/w/%EB%82%98%EC%97%B0(TWICE)"}]},{"title":"1번","thumbnail":{"imageUrl":"http://k.kakaocdn.net/dn/APR96/btqqH7zLanY/kD5mIPX7TdD2NAxgP29cC0/1x1.jpg"}},{"thumbnail":{"imageUrl":"http://dev-mk.kakao.com/dn/bot/scripts/with_barcode_blue_1x1.png","width":800,"height":800},"imageTitle":{"title":"DOFQTK","description":"Boarding Number"},"itemList":[],"itemListAlignment":"right","itemListSummary":{"title":"total","description":"$4,032.54"},"title":"title","description":"desc","buttons":[{"label":"View Boarding Pass","action":"webLink","webLinkUrl":"https://namu.wiki/w/%EB%82%98%EC%97%B0(TWICE)"}],"buttonLayout":"vertical"},{"description":"커머스카드","price":15000,"currency":"","thumbnails":[{"imageUrl":"http://dev-mk.kakao.com/dn/bot/scripts/with_barcode_blue_1x1.png"}]}],"header":{"title":"오늘 공지 n개","description":"n개를 더 불러왔습니다!","thumbnail":{"imageUrl":"https://"}}}}],"quickReplies":[{"action":"message","label":"빠른 응답","messageText":"빠른 응답 ㅋㅋ"}]},"version":"2.0"}"#;
assert_eq!(serialized, serde_json::to_string(&result).expect("Failed"));
}
#[test]
fn multiple_outputs_test() {
let mut result = Template::new();
result.add_qr(QuickReply::new(
"빠른 응답",
"빠른 응답 ㅋㅋ",
));
let mut carousel = Carousel::new().set_type(BasicCard::id());
for i in 0..5 {
let basic_card = BasicCard::new()
.set_title(format!("{}번", i))
.set_thumbnail(
"http://k.kakaocdn.net/dn/APR96/btqqH7zLanY/kD5mIPX7TdD2NAxgP29cC0/1x1.jpg"
);
carousel.add_card(basic_card.build_card());
}
result.add_output(carousel.build());
let simple_text = SimpleText::new("심플 텍스트 테스트");
result.add_output(simple_text.build());
let serialized = r#"{"template":{"outputs":[{"carousel":{"type":"basicCard","items":[{"title":"0번","thumbnail":{"imageUrl":"http://k.kakaocdn.net/dn/APR96/btqqH7zLanY/kD5mIPX7TdD2NAxgP29cC0/1x1.jpg",}},{"title":"1번","thumbnail":{"imageUrl":"http://k.kakaocdn.net/dn/APR96/btqqH7zLanY/kD5mIPX7TdD2NAxgP29cC0/1x1.jpg"}},{"title":"2번","thumbnail":{"imageUrl":"http://k.kakaocdn.net/dn/APR96/btqqH7zLanY/kD5mIPX7TdD2NAxgP29cC0/1x1.jpg"}},{"title":"3번","thumbnail":{"imageUrl":"http://k.kakaocdn.net/dn/APR96/btqqH7zLanY/kD5mIPX7TdD2NAxgP29cC0/1x1.jpg"}},{"title":"4번","thumbnail":{"imageUrl":"http://k.kakaocdn.net/dn/APR96/btqqH7zLanY/kD5mIPX7TdD2NAxgP29cC0/1x1.jpg"}}]}},{"simpleText":{"text":"심플 텍스트 테스트"}}],"quickReplies":[{"action":"message","label":"빠른 응답","messageText":"빠른 응답 ㅋㅋ"}]},"version":"2.0"}"#;
// Carousel BasicCards 뒤 SimpleText 발화
assert_eq!(serialized, result.to_string());
}
TODO
- use PyO3 to export this library in Python
How to make codes more maintainable?
pub fn set_field<T: Into<String>>(mut self, field: &str, value: T) -> Self {
match field {
"desc" | "description" => self.content.description = Some(value.into()),
"title" => self.content.title = Some(value.into()),
"thumbnail" => self.content.thumbnail.image_url = value.into(),
"link" => self.content.thumbnail.link = Some(Link { web: value.into() }),
"fixed_ratio" => self.content.thumbnail.fixed_ratio = value.into(),
"width" => self.content.thumbnail.width = Some(value.into()),
"height" => self.content.thumbnail.height = Some(value.into()),
_ => {}
}
self
}
Dependencies
~0.7–1.6MB
~33K SLoC