18リリース

0.3.7 2024年4月7日
0.3.6 2023年5月10日
0.3.4 2022年8月3日
0.3.3 2022年6月12日
0.2.2 2021年7月31日

#321 in ウェブプログラミング

Download history

879ダウンロード/月

MITライセンス

57KB
980

kakao-rs on crates.io kakao-rs on docs.rs

カカオトークチャットボットビルダー

Rust言語専用

紹介

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データバインド

例) ユーザーの発話文を取得: 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

  • PyO3を使用してこのライブラリをPythonでエクスポートする

コードをより保守的になる方法は?

    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
    }

依存関係

~0.7–1.6MB
~35K SLoC