Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Go + Google Cloud Functions を使ったSlackのThreadにも対応したbotを簡単に作る方法

Go + Google Cloud Functions を使ったSlackのThreadにも対応したbotを簡単に作る方法

Slack Dev Meetup Tokyo #1 LT 資料です。

ktykogm

July 25, 2019
Tweet

More Decks by ktykogm

Other Decks in Programming

Transcript

  1. (P(PPHMF$MPVE'VODUJPOTΛ࢖ͬ
    ͨ4MBDLͷUISFBEʹ΋ରԠͨ͠CPUΛ؆
    ୯ʹ࡞Δํ๏
    λΠτϧ͕௕͗ͨ͢LUZLPHN
    4MBDL%FW.FFUVQ5PLZP-5ࢿྉ

    View Slide

  2. XIPBNJ
    w !LUZLPHN LPHVNB

    w ϝϧΧϦ43&
    w ࠷ۙ(PΛۀ຿Ͱͨ·ʹॻ͍͍ͯΔϨϕϧ
    w ʮ4MBDL(PͳΒͦͷ͘Β͍؆୯ʹԼ४උͰ͖ΔΑʯͱ͍͏͜ͱΛ఻
    ͑ʹདྷ·ͨ͠
    w 43&-PVOHFͱ͍͏ίϛϡχςΟͷӡӦ׆ಈΛ͍ͯ͠·͢ɻ

    View Slide

  3. ຊ೔఻͍͑ͨ͜ͱ
    w 4MBDL(P$MPVE'VODUJPOTͷ૬ੑ
    w (PΛ࢖ͬͨ4MBDLͷ5ISFBEରԠํ๏
    w (Pք۾Ͱ͸Α͘஌ΒΕͨOMPQFTTMBDL͕DPPPPPPPPM
    w ͦΕΒ#PU ͦΕҎ֎΋
    ࡞੒͸ɺαʔόͱӡ༻අΛ༻ҙͤͣʹؾָʹ࢝Ί
    ΒΕΔ

    View Slide

  4. $MPVE'VODUJPOTͱ͸
    w ΠϕϯτυϦϒϯͳαʔόϨείϯϐϡʔτϓϥοτ
    ϑΥʔϜ
    w (BUFXBZ͸ແͯ͘΋ಈ͘
    w &YUFOTJCMF4FSWJDF1SPYZ &41
    Λ࢖ͬͯ(BUFXBZ
    FOEQPJOUߏ੒΋΋ͪΖΜͰ͖·͢ɻ
    w ͦͷ··ؔ਺ΛઃஔͰ͖Δ HPCVJMEෆཁ

    w ࣗಈతʹεέʔϧ׌ͭɺ)"
    w ଞͷ($1αʔϏεʹ઀ଓ֦ͯ͠ு͕Մೳ
    w ແྉ࿮͕ଘࡏ͢Δ
    w ݄ؒͰɺສݺͼग़͠ ສඵ (#ԼΓτϥ
    ϑΟοΫ ೥݄ʹ(PSVUJNF͕CFUBSFMFBTF
    IUUQTDMPVEHPPHMFDPNGVODUJPOT

    View Slide

  5. $MPVE'VODUJPOT
    ଞͷ($1αʔϏεʹ઀ଓ֦ͯ͠ு͕Մೳ
    w $MPVE1VC4VC
    w $MPVE4UPSBHF
    w )551
    w 4UBDLESJWFS-PHHJOH
    w 'JSFCBTF
    w $MPVE42- $POOFDU71$ FUD

    View Slide

  6. ࣄલ४උ
    w 4MBDLͷ༻ҙ
    w (PPHMF$MPVE1MBUGPSN ($1
    ͷΞΧ΢ϯτΛ࡞੒
    w HDMPVEίϚϯυ͕࢖͑ΔΑ͏ʹ(PPHMF$MPVE4%,Λ*OTUBMM
    w IUUQTDMPVEHPPHMFDPNTELJOTUBMM
    w HDMPVE͸ݱࡏ1ZUIPO͕ඞཁͳͷͰɺQZFOW౳ͰXPSLJOH
    EJSFDUPSZʹQZFOWMPDBMYͰ؅ཧ͢Δͱྑ͍Ͱ͢

    View Slide

  7. ࠓճͷߏ੒ͱ؀ڥʹ͍ͭͯ
    w ࠓճ͸-5ͳͷͰλΠτϧʹ͋Δ4MBDL5ISFBEʹରԠͨ͠#PUΛ࡞੒͢Δ
    ํ๏ͱͦͷઆ໌͚ͩʹ಺༰Λ΄΅ߜΓ·͢
    w 4MBDLࣗମ͸4MBTI$PNNBOE΍#PU΁ͷNFOUJPOʹΑΔ൓ԠͳͲ༷ʑ
    ͳํͰରԠͰ͖·͢
    w 4MBDLͷ35."1*Λ࢖༻
    w (PͷOMPQFTTMBDLNPEVMFΛ࢖༻

    View Slide

  8. ࠷খ$PEF
    package gcf_slack_sample
    import (
    "net/http"
    "os"
    "strings"
    "github.com/nlopes/slack"
    )
    var (
    threadTs slack.RTMsgOption
    )
    type SlackParams struct {
    accessToken string
    botUserID string
    rtm *slack.RTM
    }
    func PostMessage(w http.ResponseWriter, r *http.Request) { // Ҿ਺͸০ΓɻCloud Functionͷhttp trigger Ͱඞཁ
    params := SlackParams{
    accessToken: os.Getenv("ACCESS_TOKEN"),
    botUserID: "",
    }
    api := slack.New(params.accessToken)
    params.rtm = api.NewRTM()
    go params.rtm.ManageConnection()
    go func() {
    for msg := range params.rtm.IncomingEvents {
    switch ev := msg.Data.(type) {
    case *slack.ConnectedEvent:
    params.botUserID = ev.Info.User.ID
    case *slack.MessageEvent:
    if !strings.Contains(ev.Msg.Text, params.botUserID) {
    continue
    }
    threadTs = slack.RTMsgOptionTS(ev.ThreadTimestamp)
    params.rtm.SendMessage(params.rtm.NewOutgoingMessage("Sample TEST", ev.Channel, threadTs))
    }
    }
    }()
    ࣮͸͜Ε͚ͩͰɺຊ୊
    ͷʮ4MBDLͷ5ISFBEʹ
    ରԠͨ͠#PUPO
    $MPVE'VODUJPOTΛ(P
    Ͱ࡞Δʯ͜ͱ͸Մೳ
    WBSTZBNM͸ߦ͚ͩ$SFEFOUJBM؅ཧ
    Ͱผ్͋Γ·͢ɻޙ΄Ͳઆ໌

    HPNPEVMFTͷTFUVQ౳ͷઆ໌͸ল͖
    ·͢

    View Slide

  9. த਎ͷઆ໌

    View Slide

  10. FOEQPJOUؔ਺
    w (PPHMF$MPVE'VODUJPOTͷ࢓༷
    ͰɺNBJOؔ਺Ҏ֎͕ඞཁʹͳΓ·
    ͢ɻ
    w ྫͰ͸ɺ1PTU.FTTBHF

    w Ͳ͔͜Β։࢝͞ΕΔ͔Θ͔Γ΍͍͢Α
    ͏ʹNBJOHP΍GVODUJPOHPͱ͍͏
    pMF໊ʹͯ͠ҎԼͷΑ͏ʹ։࢝͢Δؔ
    ਺Λ࡞Γ·͢
    package gcf_slack_sample // <= ࣮͸ԿͰ΋ok
    // ..
    func PostMessage(w http.ResponseWriter, r
    *http.Request) { // Ҿ਺͸০ΓɻCloud
    Functionͷhttp trigger Ͱඞཁ
    params := SlackParams{
    accessToken: os.Getenv("ACCESS_TOKEN"),
    botUserID: "",
    }

    View Slide

  11. 4MBDLͷ5ISFBEରԠ
    w TMBDL35.TH0QUJPO
    w ͜Ε͸ޙʹઆ໌͢Δ35.TH0QUJPO54 FW5ISFBE5JNFTUBNQ
    ʹؔ܎͠
    ·͢ɻ
    var (
    threadTs slack.RTMsgOption
    )

    View Slide

  12. 4MBDLͷ5ISFBEରԠ
    ҎԼ͕4MBDLͷ5ISFBEͰͦͷ࣌ʹऔಘ͞ΕΔ5ISFBE5JNF4UBNQΛ࣋ͪ·
    ͢
    ͜ͷ5ISFBE5JNF4UBNQΛར༻͢Δ͜ͱͰ#PU͕4MBDLͷ5ISFBEʹରԠ͢
    ΔΑ͏ʹͳΓ·͢
    threadTs = slack.RTMsgOptionTS(ev.ThreadTimestamp)

    View Slide

  13. 4MBDLͷ5ISFBEରԠ
    35.TH0QUJPO54 FW5ISFBE5JNFTUBNQ
    ͱ͸Կͳͷ͔
    go func() {
    for msg := range s.rtm.IncomingEvents {
    switch ev := msg.Data.(type) {
    ͜ͷFW͸ɺͦͷલͷ35.ͷ*ODPNJOH&WFOUT͔Β&WFOUUZQF͝ͱʹTXJUDI͠ɺͦͷ&WFOUEBUBΛFWʹೖΕ͍ͯ·͢
    case *slack.MessageEvent:
    // ..
    threadTs = slack.RTMsgOptionTS(ev.ThreadTimestamp)
    .FTTBHF&WFOUΛड͚औͬͨλΠϛϯάͰɺͦͷૹ৴ݩͷTMBDLͷUISFBEʹରԠ͢ΔͨΊʹ
    ͦͷ࣌఺ͷ5ISFBE5JNF4UBNQΛ࢖༻͠·͢
    ͢ͳΘͪɺ͜ͷ.FTTBHF&WFOU͕࢝·ͬͨ࣌ͷ5ISFBE5JNFTUBNQ͕ೖΓ·͢ɻ

    View Slide

  14. OMPQFTTMBDLଆͷྲྀΕ
    ϖʔδ਺ͷؔ܎্ɺOMPQFTTMBDLଆͷઆ໌͸ׂѪ͠·͕͢ྲྀΕ͚ͩॻ͘ͱҎԼͰ͢ɻ
    35.TH0QUJPO54
    Ͱɺ.FTTBHF&WFOUΛड͚औͬͨͱ͖ͷ5ISFBE5NFTUBNQΛૹΔ
    0VUHPJOH.FTTBBHF\5ISFBE5JNFTUBNQUISFBE5JNFTUBNQ^ʹઃఆ͞ΕΔ
    35.TH0QUJPOܕͰฦ͢
    1BDLBHFWBSJBCMFͰࢦఆͨ͠ม਺UISFBE5T͸ɺͦͷ35.TH0QUJPOܕͱͳΔ


    IUUQTHJUIVCDPNOMPQFTTMBDLCMPCFBFDEFFCDEEGD
    NFTTBHFTHP--

    View Slide

  15. ड͚औͬͨ5ISFBE5JNF4UBNQΛ࢖͏
    s.rtm.NewOutgoingMessage()
    ͜ͷؔ਺͸ɺTMBDL35.ͰҎԼͷΑ͏ʹఆٛ͞Ε͍ͯ·͢ɻ
    func (rtm *RTM) NewOutgoingMessage(text string, channelID string,
    options ...RTMsgOption) *OutgoingMessage {
    // ..
    return &msg
    4MBDLͰ#PU͕࿩͢.FTTBHFΛೖΕΔؔ਺

    View Slide

  16. ड͚औͬͨ5ISFBE5JNF4UBNQΛ࢖͏
    ҎԼͰUISFBE5JNF4UBNQΛ0QUJPOͰࢦఆ͢Ε͹ྑ͍Θ͚Ͱ͢ɻ
    s.rtm.NewOutgoingMessage(message, ev.Channel, threadTs)

    View Slide

  17. ड͚औͬͨ5ISFBE5JNF4UBNQΛ࢖͏
    s.rtm.SendMessage(s.rtm.NewOutgoingMessage(message, ev.Channel, threadTs))
    ࣮ࡍʹ.FTTBHFΛૹΔͨΊʹ͸ҎԼͷΑ͏ʹ͠·͢ɻ

    View Slide

  18. ͜ΕΛ$MPVE'VODUJPOTʹ৐ͤ
    ͯ4MBDLCPUͱͯ͠ొ࿥͢Δ

    View Slide

  19. 4MBDLͰઃఆ
    4MBDL"QQͷ࡞੒ɺొ࿥͔Βɻ
    wิ଍
    w֤छͷઆ໌ʹ͍ͭͯ͸ҎԼ͕Θ͔Γ΍͍͢Ͱ͢
    wIUUQTRJJUBDPNOBNVUBLBJUFNT
    BDBG
    IUUQTBQJTMBDLDPNBQQTɹΛ։͖·͢

    View Slide

  20. 4MBDLͰઃఆ
    $SFBUF/FX"QQTΛ։
    ͍ͯ࡞੒Λ։࢝͠·͢
    ͜͜Ͱ͸UFTUCPUͱ͍͏໊
    લͰొ࿥͠·͢
    %FWFMPQNFOU4MBDL
    8PSLTQBDFΛબ୒͠·͢

    View Slide

  21. 4MBDLͰઃఆ
    #PU6TFSTΛΫϦοΫ͠
    ͯɺ"EEB#PUVTFSΛΫ
    ϦοΫ
    %JTQMBZOBNFͱ%FGBVMU
    VTFSOBNFͰ͸ɺͲͷΑ͏
    ͳจࣈ਺Ͱ΋ϋΠϑϯແ͠
    ʹม׵͞ΕΔ
    ϋΠϑϯ͕ඞཁͩͬͨΒ௥Ճ͍ͯͩ͘͠͞
    VTFSOBNF͸จࣈ·Ͱͷ੍ݶ͕͋Γ·
    ͢ɻ

    View Slide

  22. 4MBDLͰઃఆ
    4DPQFTʹ
    DIBUXSJUFCPUΛ௥Ճ͢Δ
    4BWF$IBOHFTͰอଘ
    ಉ͡ϖʔδͷҰ൪্ʹ͋Δ
    A*OTUBMMBQQUPXPSLTQBDFAΛ
    ΫϦοΫͯ͠"VUIPSJ[FE͢Δɻ
    0BVUI1FSNJTTJPOTઃఆ

    View Slide

  23. 4MBDLͰઃఆ
    #PU6TFS0"VUI"DDFTT5PLFOΛऔ
    ಘͯ͠ɺWBSTZBNMʹॻ͘
    0BVUI1FSNJTTJPOTઃఆ
    ACCESS_TOKEN: xoxb-XXXXXXXXXXXXXXXXXXXX
    WBSTZBNM HJUJHOPSFʹೖΕ͓͖ͯ·͢

    View Slide

  24. (PPHMF$MPVE'VODUJPOTʹEFQMPZ
    HDMPVEGVODUJPOTEFQMPZUFTUCPUa
    FOUSZQPJOU1PTU.FTTBHFa
    SVOUJNFHPa
    FOWWBSTpMFWBSTZBNMa
    USJHHFSIUUQ

    View Slide

  25. (PPHMF$MPVE'VODUJPOTʹEFQMPZ
    ͠͹Β͘଴ͭͱEFQMPZ͕׬ྃ͠·͢ɻ
    IUUQT5SJHHFS
    VSMIUUQTSFHJPOQSPKFDUDMPVEGVODUJPOTOFUUFTUCPU
    TUBUVT"$5*7&
    ͷΑ͏ͳIUUQT5SJHHFS͕ൃߦ͞Ε·͢ͷͰɺ͜ΕΛΫϦοΫ͠·͢ɻ

    View Slide

  26. ͜Ε͚ͩͰ׬ྃͰ͢
    TMBDLͷDIBOOFMʹ#PUΛ*OWJUF͠·͢ɻ

    View Slide

  27. #PUΛݺͼग़͢

    View Slide

  28. #PUΛݺͼग़͢
    5ISFBEʹ#PUͷ
    .FOUJPOΛඈ͹͢ͱͦͷ
    5ISFBEʹରԠ͍ͯ͠·
    ͢ɻ
    ·ͨɺ5ISFBEҎ֎ʹ͸
    ͦͷDIBOOFMʹਖ਼֬ʹ
    #PU͕Ԡ౴Λฦ͍ͤͯ·
    ͢ɻ

    View Slide

  29. ิ଍
    w ΋ͪΖΜࠓճ͸-5Ͱઆ໌༻Ͱۃ୺ʹ୹ͨ͘͠$PEFͰ͢
    w ࣮ࡍʹ࢖͏ͱ͖͸௕ظӡ༻Λߟ͑ͯॺ໊ͳΜ͔΋͚ͭΔͱྑ͍ͱࢥ͍·͢
    w 4JHOJOHTFDSFU

    View Slide

  30. ·ͱΊ
    w 4MBDLCPUͷԼ४උΛ(PͰ΋؆୯ʹग़དྷΔ
    w 5ISFBEରԠ͸ɺ
    TMBDL35.TH0QUJPO54 FW5ISFBE5JNFTUBNQ
    Λऔಘͯ͠࢖͏
    ͚ͩ
    w 4MBDLCPU(P$MPVE'VODUJPOTͷ૬ੑ͸ѱ͘ͳ͍
    w ࡞Γ΍͍֦͢ுੑ͕͋Δ
    The Go gopher was designed by Renee French. (http://reneefrench.blogspot.com/)

    View Slide

  31. ࠷ޙʹ
    IUUQTHJUIVCDPNLPHVNBHDGMFBTUTMBDLTBNQMFGPS-5

    View Slide

  32. ͝੩ௌ͋Γ͕ͱ͏͍͟͝·͠
    ͨɻ

    View Slide