본문 바로가기

DevOps/Slack & API Gateway

Slack Slash Command를 통해 집 근처 약국 마스크 수량 알아보기

Slack Slash Command + API Gateway + Lambda를 통해 간단하게 입력한 주소 근처 마스크 수량을 알아보도록 하겠습니다.

 

전체 구성도는 아래와 같습니다.

 

 

구성도

 

Kakao Geocode REST API를 통해 주소 입력 -> 위도, 경도를 받아 마스크 Open API를 콜해 알아보는 방식입니다.

따라서 먼저, Kakao Geocode REST API를 쓰기위해 API Key를 받도록 하겠습니다.

 

https://developers.kakao.com/docs/restapi/local

 

Kakao Developers_

더 나은 세상을 꿈꾸고 그것을 현실로 만드는 이를 위하여 카카오에서 앱 개발 플랫폼 서비스를 시작합니다.

developers.kakao.com

먼저 카카오 API 위의 링크에 접속해주도록 하겠습니다.

 

이후 로그인하시고

 

 

어플리케이션을 만들어서 REST API Key를 기억해주시기 바랍니다.

 


 

자 다음은 Lambda를 구축하도록 하겠습니다.

 

Slash Command의 경우

명령어 호출 -> 해당링크 호출 후 3초이내 response를 받고 이후 response url로 여러번 다시 응답을 해줘야하는 구조입니다.

 

먼저 명령어 호출을 하면 이를 검사하고, 응답을 바로 보내준 후, 슬래쉬 커맨드에 따라서 올바른 수행을 할 수 있는 람다를 만들겠습니다.

 

비동기로 람다를 다시 콜해줘야하기 때문에 해당롤이 있게끔만 구성해주시면 됩니다.

 

 

이제 람다를 생성합니다.

 

 

람다에 들어갈 코드는 아래와 같습니다.

슬랙 봇 토큰값은 맨 마지막에 구성하면서 넣어주도록 하겠습니다.

 

import json
import boto3

def check_token(token):
    token_list = ["슬랙 봇 토큰값"]
    if token in token_list:
        return True
    else:
        return False

def lambda_handler(event, context):
    print(event)
    token = event['token']
    client = boto3.client('lambda')
    if check_token(token):
        response = client.invoke_async(
            FunctionName='slack-slash-command-'+event['command'][1:].lower(),
            InvokeArgs=json.dumps(event)
        )
        return {
            # "response_type": "in_channel",
            "text": "Check the Slack Token...\nSuccess Authentication!\nIt takes few seconds to process..."
        }
    else:
        return {
            # "response_type": "in_channel",
            "text": "Check the Slack Token...\nFailed Authentication\nPlease Check the Slash Command App Setting"
        }

 


 

자 이제 두번째람다를 만들겠습니다.

Slash Command의 토큰값이 인증되고나면, / 뒤에 붙인 커맨드에 따라서 알맞은 서비스가 수행되어야 할 것입니다.

여기서 requests 모듈과, slackweb 라이브러리가 필요합니다. 해당 파일은 layer에 아래와 같이 등록하여서 사용하면 됩니다.

layer는 아래 파일을 받아서 아래와 같이 만들어주세요

python.zip
0.97MB

 

mask라는 커맨드를 저는 붙일 예정이므로 아래와 같이 만들어주도록 하겠습니다.

 

 

아까 만든 layer를 추가해줍니다.

 

 

함수의 코드는 아래와 같습니다.

 

import json
import boto3
import slackweb
import requests

def slack_send(url, message):
    slack = slackweb.Slack(url=url)
    # slack.notify(response_type="in_channel", text=message)
    slack.notify(text=message)

def help():
    message = "***Mask Slash Commnand 사용법***\n"\
    "명령어 문법은 다음과 같습니다 -> '/mask find/주소/반경'\n"\
    "ex) '/mask find/난곡로 50길 13/1000' -> 난곡로50길 13 반경 1000m 약국 및 마스크 수량정보를 가져옵니다."
    print(message)
    return message

def kakao_geocode(address, meter):
    url = "https://dapi.kakao.com/v2/local/search/address.json?query=" + address
    
    headers = {
        'content-type': 'application/json; charset = UTF-8',
        'Authorization': 'KakaoAK 카카오RESTAPI키'
    }
    
    response = requests.get(url=url, headers=headers)
    result = response.json()
    lng = result['documents'][0]['address']['x']
    lat = result['documents'][0]['address']['y']
    return "storesByGeo/json?lat=" + lat + "&lng=" + lng + "&m=" + meter

def get_remain(code):
    print(code)
    if code == 'plenty':
        return '100개 이상'
    elif code == 'some':
        return '30~99개'
    elif code == 'few':
        return '2~29개'
    elif code == 'empty':
        return '1개 이하'
    else:
        return '알 수 없음'
        

def mask_open_api(path):
    url = "https://8oi9s0nnth.apigw.ntruss.com/corona19-masks/v1/"
    headers = {'content-type': 'application/json; charset = UTF-8'}
    response = requests.get(url=url + path, headers=headers)
    return response.json()

def find(address, meter):
    path = kakao_geocode(address, meter)
    result = mask_open_api(path)
    print(result)
    
    message = ""
    try:
        if result['count'] >= 1:
            stores = []
            message = address + " 반경 " + meter + "m 내의 약국 검색결과 입니다.\n\n"\
            "총 " + str(result['count']) + "개의 공적 판매처가 있습니다.\n"
            
            for store in result['stores']:
                addr = str(store['addr'])
                name = str(store['name'])
                remain = get_remain(str(store['remain_stat']))
                remain_date = str(store['stock_at'])
                message = message + "업데이트 시각 : " + str(store['created_at']) +"\n"\
                "약국명 : " + name + "\n"\
                "주소 : " + addr + "\n"\
                "입고시간 : " + remain_date + "\n"\
                "재고 :" + remain + "\n\n" 
        else:
            message = address + " 반경 " + meter + "m 내의 약국이 없습니다."
    except Exception as e:
        print(e)
        message = "Mask Open API Error"
    return message

def do_act(act):
    function = act[0]
    if function == "find":
        return find(act[1], act[2])
    elif function == "help":
        return help()
    else:
        return "Please Check the function"

def lambda_handler(event, context):
    act = event['text'].split('/')
    print(act)
    result = do_act(act)
    slack_send(event['response_url'], result)

 


 

자 이제 이 람다 함수를 콜하기 위해서 API Gateway를 만들어주도록 하겠습니다.

 

 

리소스를 아래와 같이 만들어주시고 메소드는 POST로 해주시면 됩니다.

 

 

슬래쉬 커맨드를 위해서 아래와 같이 매핑 템플릿을 설정해주셔야 합니다.

 

아래 코드를 위와 같이 입력해주시면 됩니다.

## convert HTML POST data to JSON
 
## get the raw post data from the AWS built-in variable and give it a nicer name
#set($rawAPIData = $input.path('$'))

## first we get the number of "&" in the string, this tells us if there is more than one key value pair
#set($countAmpersands = $rawAPIData.length() - $rawAPIData.replace("&", "").length())
 
## if there are no "&" at all then we have only one key value pair.
## we append an ampersand to the string so that we can tokenise it the same way as multiple kv pairs.
## the "empty" kv pair to the right of the ampersand will be ignored anyway.
#if ($countAmpersands == 0)
 #set($rawPostData = $rawAPIData + "&")
#end
 
## now we tokenise using the ampersand(s)
#set($tokenisedAmpersand = $rawAPIData.split("&"))
 
## we set up a variable to hold the valid key value pairs
#set($tokenisedEquals = [])
 
## now we set up a loop to find the valid key value pairs, which must contain only one "="
#foreach( $kvPair in $tokenisedAmpersand )
 #set($countEquals = $kvPair.length() - $kvPair.replace("=", "").length())
 #if ($countEquals == 1)
  #set($kvTokenised = $kvPair.split("="))
  #if ($kvTokenised[0].length() > 0)
   ## we found a valid key value pair. add it to the list.
   #set($devNull = $tokenisedEquals.add($kvPair))
  #end
 #end
#end
 
## next we set up our loop inside the output structure "{" and "}"
{
#foreach( $kvPair in $tokenisedEquals )
  ## finally we output the JSON for this pair and append a comma if this isn't the last pair
  #set($kvTokenised = $kvPair.split("="))
 "$util.urlDecode($kvTokenised[0])" : #if($kvTokenised[1].length() > 0)"$util.urlDecode($kvTokenised[1])"#{else}""#end#if( $foreach.hasNext ),#end
#end
}

 

자 모든 작업이 끝났다면 최종적으로 API Gateway를 배포해주겠습니다.

 

 


 

이제 배포된 URL + PATH를 복사해주시고 Slack API -> Your Apps로 가도록 합시다.

 

앱을 하나 만드시고 Slash Commands란으로 이동해주세요

 

저는 이전에도 AWS 리소스를 컨트롤하는데 사용하고있어 아래와 같습니다.

create new command를 눌러줍니다.

 

 

원하는 명령어를 적어주시고, 아까 배포하고 난 URL + PATH를 넣어줍니다.

 

 

자 그리고 마지막으로 아래 있는 기본정보에서 Verification Token 을 복사해주세요 (재생성 아니고 복사해주세요)

 

 

 

이 값을 아까 slack-slash-command-auth 람다 소스의 토큰값에 넣어주시면 됩니다.

 

이제 슬랙 채널에서 어플리케이션 추가를 하고 나면 아래와 같이 사용하실 수 있습니다.

 

 

긴 글 읽어주셔서 감사드리고 돈들이지 않고 유용하게 사용하시면 좋을 것 같습니다.