Esempio n. 1
0
def create_mail(sender: str, to: str, subject: str, msg_html: str,
                msg_plain: str) -> dict:
    """Create an email message.

    I got this to work thanks to
    https://stackoverflow.com/questions/37201250/sending-email-via-gmail-python

    Args:
        sender (str): Mail address of sender
        to (str): Destination mail address
        subject (str): Mail subject
        msg_html (str): Html content for the mail
        msg_plain (str): String version of the mail
    Returns:
        Message body in mime format
    """
    logger.info('creating mail')
    msg = MIMEMultipart('alternative')
    msg['Subject'] = subject
    msg['From'] = sender
    msg['To'] = to
    msg.attach(MIMEText(msg_plain, 'plain'))
    msg.attach(MIMEText(msg_html, 'html'))
    raw = base64.urlsafe_b64encode(msg.as_bytes())
    raw = raw.decode()
    body = {'raw': raw}
    return body
Esempio n. 2
0
def create_file(source_file_path: str,
                file_name: str = None,
                parent_folder_id: str = None,
                service=None) -> dict:
    """Upload a file from the local machine into a new file on the drive

    Args:
        source_file_path (str): Path to the file to upload
        file_name (str): If None, the name of the file will be used
        parent_folder_id (str): Id of a folder to put the copy into. if none
            is specified, the copy will be at the root of the drive.
        service (optional, drive-api-service): the service to use. Default:
            the result of `default_service()`
    Returns:
        dict containing the id and name of the created file
    """
    logger.info('creating file')
    source_file_path = Path(source_file_path).expanduser()
    if file_name is None:
        file_name = source_file_path.name
    request_body = {'name': file_name, 'mimeType': '*/*'}
    media_body = MediaFileUpload(
        str(source_file_path),
        mimetype='*/*',
        resumable=False,
    )
    if parent_folder_id is not None:
        request_body["parents"] = [parent_folder_id]
    return service.files().create(
        body=request_body,
        media_body=media_body,
    ).execute()
Esempio n. 3
0
def get_messages(query: str, service=None) -> list:
    """List messages matching the specified query
    Args:
        query (str): a gmail-message-search-query. Documentation link:
            https://support.google.com/mail/answer/7190?hl=en
        service (optional, gmail-api-service): the service to use. Default:
            the result of `default_service()`
    Returns:
        List of Messages that match the criteria of the query. Note that the
        returned list contains Message IDs, you must use get with the
        appropriate ID to get the details of a Message.
    """
    logger.info('getting mails')
    response = service.users().messages().list(userId='me', q=query).execute()
    messages = []
    if 'messages' in response:
        messages.extend(response['messages'])

    while 'nextPageToken' in response:
        page_token = response['nextPageToken']
        response = service.users().messages().list(
            userId='me', q=query, pageToken=page_token).execute()
        messages.extend(response['messages'])

    return messages
Esempio n. 4
0
def get_files(query: str, service=None) -> list:
    """Query google drive for files matching `query`

    If no accessible file matches the query, returns an empty list.
    Args:
        query (str): a drive-file-search-query. Documentation link:
            https://developers.google.com/drive/api/v3/search-parameters
        service (optional, drive-api-service): the service to use. Default:
            the result of `default_service()`
    Returns:
        list of dict, file information records. Contains the name & id
        of the first file in the drive matching the query
        and accessible within the oauth permissions of the script
    """
    logger.info('getting files')
    logger.debug(f'query = {query}')
    page_token = None
    files = []
    # We have to cycle on all the pages of the drive,
    while True:
        file_candidates = service.files().list(
            q=query,
            fields="nextPageToken, files(id, name)",
            pageToken=page_token).execute()
        files += file_candidates.get('files', [])

        page_token = file_candidates.get('nextPageToken', None)
        if page_token is None:
            break
    return files
Esempio n. 5
0
def send_file(mail_address: str,
              mail_subject: str,
              file_id: str,
              service=None,
              sender: str = '*****@*****.**') -> dict:
    """Send a mail with a link to a google doc

    Args:
        mail_address (str): Destination mail address
        mail_subject (str): Mail subject
        file_id (str): Id of the file to send
        service (optional, gmail-api-service): the service to use. Default:
            the result of `default_service()`
        sender (str): Mail address of the sender
    Returns:
        dict, information about the message used to send the file, including
        it's id
    """
    logger.info('sending file')
    message = create_mail(
        sender, mail_address, mail_subject,
        f"<a href=https://docs.google.com/document/d/{file_id}>"
        f"Project description</a>",
        f"https://docs.google.com/document/d/{file_id}")
    return send('me', message, service=service)
Esempio n. 6
0
def default_service():
    """Lazy getter for the default drive-api-service to use

    Returns:
        The official python wrapper around the drive api
    """
    logger.info("instantiating google drive service")
    return build('drive', 'v3', http=get_creds().authorize(Http()))
Esempio n. 7
0
def delete_file(file_id: str, service=None):
    """copy a file in the user's drive

    Args:
        file_id (str):
        service (optional, drive-api-service): the service to use. Default:
            the result of `default_service()`
    Returns:
        an empty string if successful
    """
    logger.info("deleting file")
    return service.files().delete(fileId=file_id).execute()
Esempio n. 8
0
def get_labels(service=None) -> list:
    """Fetches all existing labels in the user's inbox

    Args:
        service (optional, gmail-api-service): the service to use. Default:
            the result of `default_service()`
    Returns:
        list of dict, label information records. The id and name for each
        label existing in the user's inbox.
    """
    logger.info('fetching labels')
    return service.users().labels().list(userId='me').execute().get(
        'labels', [])
Esempio n. 9
0
def download_file(file_id: str, service=None):
    """Download a file and return it in a variable

    Args:
        file_id (str): Id of the file to download
        service (optional, drive-api-service): the service to use. Default:
            the result of `default_service()`
    Returns:
        the content of the file
    """
    logger.info('downloading file')

    return service.files().get_media(fileId=file_id).execute()
Esempio n. 10
0
def send(user_id: str, mime_msg: dict, service=None) -> dict:
    logger.info('sending mail')
    """Send an email message.
    
    Args:
        user_id (str): User's email address. The special value
            "me" can be used to indicate the authenticated user.
        mime_msg (mime message): Message to be sent
        service (optional, gmail-api-service): the service to use. Default: 
            the result of `default_service()`
    Returns:
        dict containing information about the message sent, including it's id
    """
    result = service.users().messages().send(userId=user_id,
                                             body=mime_msg).execute()
    return result
Esempio n. 11
0
def create_folder(folder_name: str, service=None) -> dict:
    """Create a new folder in the user's drive
    Args:
        folder_name (str): name of the folder to create
        service (optional, drive-api-service): the service to use. Default:
            the result of `default_service()`

    Returns:
        dict, id and name of the folder
    """
    logger.info('creating folder')
    file_metadata = {
        'name': folder_name,
        'mimeType': 'application/vnd.google-apps.folder'
    }
    return service.files().create(body=file_metadata,
                                  fields='id, name').execute()
def get_creds(config: Config = default):
    """Check that the SSO token is valid. If not, asks for a new one.

    Asking for a new token is done by opening the sing-in-with-google-tab in
    the browser. The google account linked to in client_id.json is
    pre-selected.

    The `client_id.json` corresponds to what you can download from
    https://console.developers.google.com/apis/credentials.

    I took this function almost as is from the official google gmail api doc:
    https://developers.google.com/gmail/api/quickstart/python.

    Note: `oauth2client.tools` bugs if `sys.argv` are specified. This
    function fixes that.

    Args:
        config: a config element as defined in the `config.py` package

    Returns:
        credentials that the api can work with
    """
    config_path = config.credential_path
    scopes = config.scopes

    logger.info('loading token')
    logger.debug(f'config_path: {config_path}')
    config_path = Path(config_path).expanduser()
    store = file.Storage(config_path / 'token.json')
    creds = store.get()

    if not creds or creds.invalid:
        # Ask the user to give the correct permissions.
        logger.info('loading credentials')
        flow = client.flow_from_clientsecrets(config_path / 'client_id.json',
                                              scopes)

        arguments = sys.argv
        sys.argv = sys.argv[0:1]
        # This line is why we need to remove the arguments from sys.argv
        # If you find a better way to get it to work, i'm buying it
        creds = tools.run_flow(flow, store)
        sys.argv = arguments

    return creds
Esempio n. 13
0
def move_to_trash(message_id: str, service=None):
    """Mark a message with a label, as read and archive it
    Args:
        message_id (str): Id of the message to trash
        service (optional, gmail-api-service): the service to use. Default:
            the result of `default_service()`
    Returns:
        the api request's result
    """
    logger.info('moving mail to trash')

    return service.users().messages().modify(id=message_id,
                                             userId='me',
                                             body={
                                                 "addLabelIds": [
                                                     'TRASH',
                                                 ]
                                             }).execute()
Esempio n. 14
0
def archive_message(message_id: str, extra_labels: str = None, service=None):
    """Mark a message with a label, as read and archive it
    Args:
        message_id (str): Id of the message to archive
        extra_labels (str): Labels to add to the message in the form
            "label_1,label2"
        service (optional, gmail-api-service): the service to use. Default:
            the result of `default_service()`
    Returns:
        the api request's result
    """
    logger.info('archiving mail')
    body = {"removeLabelIds": ['UNREAD', 'INBOX']}
    if extra_labels is not None:
        body["addLabelIds"] = [extra_labels]
    return service.users().messages().modify(id=message_id,
                                             userId='me',
                                             body=body).execute()
Esempio n. 15
0
def create_label(label_name: str, service=None) -> dict:
    """Create a label with the specified name in the user's inbox

    Args:
        label_name(str): Name of the label to create
        service (optional, gmail-api-service): the service to use. Default:
            the result of `default_service()`
    Returns:
        dict containing the id and name of the created label
    """
    logger.info('creating label')
    label = service.users().labels().create(userId='me',
                                            body={
                                                'messageListVisibility':
                                                'show',
                                                'name': label_name,
                                                'labelListVisibility':
                                                'labelShow'
                                            }).execute()
    return label
Esempio n. 16
0
def update_file(source_file_path: str,
                file_id: str,
                file_name: str = None,
                parent_folder_id: str = None,
                service=None) -> dict:
    """Upload a file from the local machine into an existing file on the drive

    Args:
        source_file_path (str): Path to the file to upload
        file_id (str): Id of the file to update
        file_name (str): If None, the name of the file will be used
        parent_folder_id (str): If None, sets the file's folder to root. If
            you want to keep an existing folder, you will have to include
            it's id.
        service (optional, drive-api-service): the service to use. Default:
            the result of `default_service()`
    Returns:
        dict containing the id and name of the created file
    """
    logger.info('updating file')
    source_file_path = Path(source_file_path).expanduser()
    if file_name is None:
        file_name = source_file_path.name
    request_body = {'name': file_name, 'mimeType': '*/*'}
    media_body = MediaFileUpload(
        str(source_file_path),
        mimetype='*/*',
        resumable=False,
    )
    if parent_folder_id is not None:
        request_body["parents"] = [parent_folder_id]
    return service.files().update(
        fileId=file_id,
        body=request_body,
        media_body=media_body,
    ).execute()
Esempio n. 17
0
def copy_file(source_file_id: str,
              new_file_name: str,
              parent_folder_id: str = None,
              service=None) -> dict:
    """Duplicate a file inside the user's drive

    Args:
        source_file_id (str): Id of the file to copy
        new_file_name (str): Name to give to the copy
        parent_folder_id (str): Id of a folder to put the copy into. if none
            is specified, the copy will be at the root of the drive.
        service (optional, drive-api-service): the service to use. Default:
            the result of `default_service()`
    Returns:
        dict containing the id and name of the created file
    """
    logger.info('copying file')
    request_body = {
        "name": new_file_name,
    }
    if parent_folder_id is not None:
        request_body["parents"] = [parent_folder_id]
    return service.files().copy(fileId=source_file_id,
                                body=request_body).execute()
Esempio n. 18
0
def default_service():
    """Lazy getter for the default gmail-api-service to use
    """
    logger.info("instantiating gmail service")
    return build('gmail', 'v1', http=get_creds().authorize(Http()))