본문 바로가기
모바일 SW 개발/REST API

Google gmail API 사용 방법 (3) - Sample code

by 모바일 SW 개발자 Kibua20 2020. 7. 5.
반응형

Gmail API 사용 방법에 대한 3번째 포스트입니다.  앞서 2개의 포스트에서는 1) Google API Console에서 프로젝트를 생성하고, Client ID와 sescret을 받는 과정과 2) 구글 인증 서버에 계정 로그인을 통해서 API scope에 대한 명시적 동의를 받아 서버 인증용 access token을 받는 과정을 설명하였다. 본 게시글은 access token을 사용해서 실제 Google REST API에 호출하는 방법과 샘플 코드를 제공한다.  

 

[모바일/REST API] - Google Gmail API 사용 방법 (1)

[모바일/REST API] - Google Gmail API 사용 방법 (2)

 

 

Google API 호출을 위한 전체 Flow (Web Server Application flow로 진행)

 

Gmail API 사용: curl 명령어 확인하기 

구글 API 사용하기 위해서는 API Reference 사이트에서 원하는 기능의 API를 찾아야 한다.  본 게시글에서는 Gmail 서버를 사용해서 email 전송 구현이 목표이며, 사용할 수 있는 API는 Users.Messages:send 이다.  API Reference에서는 API 호출을 위한 http Request를 위한 header paramter, Authorization scope, Response body, sample code 가 설명되어 있다. 

 

Users.Messages:sendhttps://www.googleapis.com/upload/gmail/v1/users/userId/messages/send URL로 http request의 Post함수를 사용한다. userID는 앞서 설정한 gmail 사용자 계정을 의미하고 string (e.g. kibua20@gmail.com)으로 입력해야 한다.  Request Body는 RFC 2822 양식으로 base64url encoding 값으로 raw 포맷으로 구성할 수 있고, API 에 대한 response로 성공인 경우 200 OK를 전달하고 message id를 리턴한다.  

 

(1) HTTP request
  POST https://www.googleapis.com/upload/gmail/v1/users/userId/messages/send

(2) Parameter
  userId string The user's email address. The special value me can be used to indicate the authenticated user

(3) Request body
   Property nameValueDescriptionNotesRequired Propertiesrawbytes. The entire email message in an RFC 2822 formatted and base64url encoded string. Returned in messages.get and drafts.get responses when the format=RAW parameter is supplied.

(4) Response
   If successful, this method returns a Users.messages resource in the response body.

Google API Reference - email 전송은 Users.Messages:send API를 사용한다

 

API를 Python code로 바로 변환하는 것 보다는 curl를 사용해서 http request 시 header, request body, response의 동작을 먼저 확인해서 검증 후에 code로 변환하는 것이 개발 시간을 줄이는데 도움이 된다. Google API 의 경우에는 API reference site에서 curl 명령어를 직접 사용할 수 있는 도구를 제공한다.  (API reference site의 "Try This API" )  만일 curl 이 동작이 잘된다면 curl 을 python code로 자동변환도 가능하다. (변환 사이트)

 

Google Try This API 에서는 아래와 같이 입력한다.

 

1) User ID 설정: kibua20@gmail.com

2) JSON 의 raw 내용: Email이 내용을 base64 url로 encoding 한 값이다. 아래 Python 코드에서 CreateMessage()의 return 값을 마우스로 copy & Paste로 입력한다.

3) 실행 함수를 누르면 계정 로그인 및 사용자 권한 동의 팝업이 표시되고, OK를 누르면 실제 email 이 전송된다.

4) Email 전송이 완료가 되면 구글 서버에 response 200 OK와 함께 message ID 값을 전달한다. 

 

Google Gmail API: curl 로 http request의 header, body 구성 방법을 확인할 수 있다.

 

결과적으로 curl 명령어는 아래와 같고,  Python code를 옮길 때에는 access token과 JSON의 raw 값을 채워야 한다. Access token을 사용하는 경우 API Key 설정은 필요 없다. 

 

curl --request POST \
'https://www.googleapis.com/gmail/v1/users/[USERID]/messages/send?key=[YOUR_API_KEY]' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '{"raw":""}' \
--compressed

 

 

Gmail API 사용: Python code 작성하기

Gmail API 가 curl로 http request가 정상 동작하는 것을 확인하였으면, 다음 단계는 http request내용을 그대로 Python code를 옮기는 과정이다.  아래 Python sample code는 Git Hub에서 다운로드할 수 있다. 

 

Gmail 전송을 위한 Python code는 크게 2개의 CreateMessage()와 sendmail() 함수로 구성되어 있다. CreateMessage() 함수는Email  sender 주소, 받는 사람 주소, 제목, 메시지를 받아서 base64 encoding 스트링을 구성하는 함수이고, sendmail() 함수는 curl로 확인한 http request의 header와 body를 구성하여 실제 http 전송을 실행하는 함수이다. 

 

아래 code에서는 일반 text 파일도 html를 mime type으로 전송하고, message에 html tag (e.g. <br>, <h1>)를 사용할 수 있다. 첨부 파일은 이미지, 오디오, 텍스트 파일 등으로 첨부가 가능하다.   sendmail() 함수 중간에 payload 값을 콘솔에서 copy 해서 Google API reference 사이트에 입력하면 API의 동작을 확인할 수 있다.  아래 code에서 access token과 email address는 각자 등록한 계정에 맞는 값을 사용해야 한다. 

 

https://github.com/kibua20/devDocs/tree/master/gmail

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

import base64
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import mimetypes
import os
import requests
import json
import sys

# -----------------------------------------------------------------------------------------
def CreateMessage(sender, to, subject, message_text=None, attachment_path=None):
  """Create a message for an email.

  Args:
    sender: Email address of the sender.
    to: Email address of the receiver.
    subject: The subject of the email message.
    message_text: The text of the email message.
    attachment_path: Full path for attachment (dir+filename)


  Returns:
    An object containing a base64url encoded email object.
  """
  message = MIMEMultipart()
  message['to'] = to
  message['from'] = sender
  message['subject'] = subject
  #message['cc'] = cc
  #message['bcc'] = cc
  #msg.add_header('reply-to', return_addr)

  if message_text != None:
    msg = MIMEText(message_text, 'html')
    message.attach(msg)

  if attachment_path != None:
    content_type, encoding = mimetypes.guess_type(attachment_path)

    if content_type is None or encoding is not None:
        content_type = 'application/octet-stream'
    main_type, sub_type = content_type.split('/', 1)
    if main_type == 'text':
        fp = open(attachment_path, 'rb')
        msg = MIMEText(fp.read().decode('utf8'), _subtype=sub_type)
        fp.close()
    elif main_type == 'image':
        fp = open(attachment_path, 'rb')
        msg = MIMEImage(fp.read(), _subtype=sub_type)
        fp.close()
    elif main_type == 'audio':
        fp = open(attachment_path, 'rb')
        msg = MIMEAudio(fp.read(), _subtype=sub_type)
        fp.close()
    else:
        fp = open(attachment_path, 'rb')
        msg = MIMEBase(main_type, sub_type)
        msg.set_payload(fp.read())
        fp.close()

    msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(attachment_path))
    message.attach(msg)

  try:
    return {'raw': base64.urlsafe_b64encode(message.as_string())}
  except:
    return {'raw': base64.urlsafe_b64encode(message.as_bytes()).decode('utf8')}

# -----------------------------------------------------------------------------------------
def sendmail(to, subject, message_text_html, attachment=None):
  # Your access code 
  access_token = 'ya29.aaaaaaaa'
  user_id = 'your id@gmail.com'

  URL = 'https://www.googleapis.com/gmail/v1/users/'+user_id+'/messages/send'

  request_header = {
      "Content-Type": "application/json",
      "Authorization": "Bearer " + access_token,
      "X-GFE-SSL": "yes"
  }

  payload = CreateMessage(user_id, to, subject, message_text_html, attachment)

  # validate payload on https://developers.google.com/gmail/api/v1/reference/users/messages/send
  # print (payload)
  # exit(1)

  response = requests.post(URL, headers=request_header, data=json.dumps(payload))

  print ('*** Error *** : sendmail fail')
  print(response)
  print(response.text)

# -----------------------------------------------------------------------------------------
if __name__ == "__main__":
    sendmail('receiver@gmail.com', 'subject_test', 'message_test', None)

 

관련 글:

[모바일/REST API] - Google Gmail API 사용 방법 (1)

[모바일/REST API] - Google gmail API 사용 방법 (3)

 

[모바일/Python] - Python 표준 입출력(stdin/stdout) 활용 - 리눅스 프로그램과 연동

[모바일/Python] - Python JSON 사용 시 TypeError: Object of type bytes is not JSON serializable

[모바일/Python] - Python smtplib 사용한 email 발송 예제 (gmail)

[개발환경] - [Memo] 우분투에서 gmail 활용하여 command line으로 email 전송

[모바일/REST API] - 우분투 20.04에서 Web 서버 설치 방법 (apache2, tomcat9)

 

반응형



댓글3