def attach_file(self,
                    filename,
                    name=None,
                    content_type=None,
                    comment=None):
        """attach_file Attach a file to this page

        Wrapper for the Confluence method attach_file() that includes the
        page ID of this object and is wrapped in utils.safe_service_connect().

        Parameters
        ----------
        filename : `str`
            Filename of the attachment
        name : `str`, optional
            Display name for this attachment [Default: None]
        content_type : `str`, optional
            MIME content type [Default: None]
        comment : `str`, optional
            Additional comment or description to be included [Default: None]
        """
        if not self._check_perm("CREATEATTACHMENT", "create an attachment"):
            return

        utils.safe_service_connect(
            self.instance.attach_file,
            filename,
            name=name,
            content_type=content_type,
            page_id=self.page_id,
            comment=comment,
        )
    def create(self, page_body, parent_id=None):
        """create Create a brand new Confluence page

        Summon from the depths of computing a new page.

        Parameters
        ----------
        page_body : `str`
            The body of the new Confluence page.
        parent_id : `str`, optional
            The parent page to place this under.  If none given, the new page
            will be created at the root of `self.space`. [Default: None]
        """
        if not self._check_perm("EDITSPACE", "create a page"):
            return

        # Check if it exists before we try anything
        if self.exists:
            print("Can't create a page that already exists!")
            return

        utils.safe_service_connect(
            self.instance.create_page,
            self.space,
            self.title,
            page_body,
            parent_id=parent_id,
            representation="wiki",
            editor="v1",
        )
        # Set the instance metadata (exists, page_id, etc.)
        self._set_metadata()
    def smite(self):
        """smite Kill with extreme prejudice

        Remove the Confluence page and update the instance metadata to reflect
        the new state.
        """
        if not self._check_perm("REMOVEPAGE", "remove a page"):
            return

        utils.safe_service_connect(self.instance.remove_page, self.page_id)
        self._set_metadata()
    def _read_channels(self, name):
        """_read_channels Return the Channel ID for the names channel

        Parameters
        ----------
        name : `str`
            The name of the channel

        Returns
        -------
        `str`
            The desired Channel ID
        """
        conversation_id = None

        try:
            # Call the conversations.list() method using the WebClient
            result = utils.safe_service_connect(self.client.conversations_list)
            for _ in result:
                if conversation_id is not None:
                    break
                for channel in result["channels"]:
                    if channel["name"] == name:
                        conversation_id = channel["id"]
                        break

        except slack_sdk.errors.SlackApiError as error:
            warnings.warn(
                f"An error occurred within SlackChannel._read_channels():\n{error}"
            )

        # Return the conversation ID
        return conversation_id
    def __init__(self, label=None, after=None, before=None, interactive=False):
        # Initialize basic stuff
        self.label_list = None
        self.message_list = []

        # Initialize the Gmail connection
        self.service = setup_gmail(interactive=interactive)
        self.label_id = self._lableId_from_labelName(label)
        self.query = build_query(after_date=after, before_date=before)

        # Get the list of matching messages (API: users.messages.list)
        if self.label_id:
            try:
                results = utils.safe_service_connect(
                    self.service.users()
                    .messages()
                    .list(
                        userId="me",
                        labelIds=[self.label_id],
                        q=self.query,
                        maxResults=500,
                    )
                    .execute
                )
                self.message_list = results.get("messages", [])
            except (HttpError, ConnectionError) as error:
                warnings.warn(
                    f"An error occurred within GetMessages.__init__():\n{error}"
                )
    def send(self):
        """send Send the GmailMessage

        _extended_summary_

        Parameters
        ----------
        n_tries : `int`, optional
            The number of retry attemps at sending this message  [Default: 5]

         Returns
        -------
        `dict`
            The sent message object
        """
        # Take the message object, and 64-bit encode it properly for sending
        encoded_message = base64.urlsafe_b64encode(self.message.as_bytes())
        # The sendable message is a dictionary containing the raw decoded thing
        sendable_message = {"raw": encoded_message.decode()}

        # If Gmail `Resource` was not returned earlier, try again
        if not self.service:
            self.service = setup_gmail()

        # Try to send the message (API: users.messages.send)
        try:
            return utils.safe_service_connect(
                self.service.users()
                .messages()
                .send(userId="me", body=sendable_message)
                .execute
            )
        except (HttpError, ConnectionError) as error:
            warnings.warn(f"An error occurred within GmailMessage.send():\n{error}")
            return None
    def add_label(self, label):
        """add_label Add a label to the Confluence page

        Sometimes it's helpful to have a label on a Confluence page for
        searching or sorting.  This method adds such to a page.

        Parameters
        ----------
        label : `str`
            The label to be added to the page
        """
        if not self._check_perm("EDITSPACE", "add a label"):
            return

        utils.safe_service_connect(self.instance.set_page_label, self.page_id,
                                   label)
    def _set_permdict(self):
        """_set_permdict Create a dictionary of permissions

        This method creates a dictionary of permissions for this user in this
        space.  Each item in the dictionary is boolean based on the results of
        the method confluence.get_space_permissions().

        Returns
        -------
        `dict`
            The dictionary of permissions (boolean)
        """
        perms = utils.safe_service_connect(self.instance.get_space_permissions,
                                           self.space)

        # Check to see if the authenticated user can view permissions
        if not perms:
            warnings.warn(
                f"User {self.uname} needs permission to view "
                f"permissions in space {self.space}.   Contact "
                "your Confluence administrator.",
                utils.PermissionWarning,
            )

        perm_dict = {}
        for perm in perms:
            # Set this permission as false... will update to True if needed
            perm_dict[perm["type"]] = False
            for space_perm in perm["spacePermissions"]:
                if space_perm["userName"] == self.uname:
                    perm_dict[perm["type"]] = True

        return perm_dict
    def _set_metadata(self):
        """_set_metadata Set the various instance metadata

        Especially after a page is created or deleted, this method updates the
        various instance attributes to keep current.
        """
        self.exists = utils.safe_service_connect(self.instance.page_exists,
                                                 self.space, self.title)

        # Page-Specific Information
        self.page_id = (None
                        if not self.exists else utils.safe_service_connect(
                            self.instance.get_page_id, self.space, self.title))
        self.attachment_url = (
            None if not self.exists else
            f"{self.instance.url}download/attachments/{self.page_id}/")
Exemple #10
0
    def send_message(self, message):
        """send_message Send a (text only) message to the channel

        _extended_summary_

        Parameters
        ----------
        message : `str` or `blocks[]` array
            The message to send to the Slack channel

        Returns
        -------
        `Any`
            The response from Slack
        """
        response = None
        try:
            # Call the conversations.list method using the WebClient
            response = utils.safe_service_connect(
                self.client.chat_postMessage,
                channel=self.channel_id,
                text=message
                # You could also use a blocks[] array to send richer content
            )
            # Print result, which includes information about the message (like TS)
            # print(result)
        except slack_sdk.errors.SlackApiError as error:
            warnings.warn(
                f"An error occurred within SlackChannel.send_message():\n{error}"
            )
        return response
Exemple #11
0
    def upload_file(self, file, title=None):
        """upload_file Upload a file to the channel

        _extended_summary_

        Parameters
        ----------
        file : `str` or `os.PathLike`
            The (path and) filename of the file to be uploaded.
        title : `str`, optional
            The title for the file posted [Default: None]

        Returns
        -------
        `Any`
            The response from Slack
        """
        response = None
        try:
            response = utils.safe_service_connect(
                self.client.files_upload,
                channels=self.channel_id,
                file=file,
                title=title,
            )
        except slack_sdk.errors.SlackApiError as error:
            warnings.warn(
                f"An error occurred within SlackChannel.upload_file():\n{error}"
            )
        return response
    def add_comment(self, comment):
        """add_comment Add a comment to the Confluence page

        Sometimes it's helpful to include a comment at the bottom of the
        Confluence page.  These will be signed by Nanni.  This method adds
        such to a page.

        Parameters
        ----------
        comment : `str`
            The comment to be left on the page.
        """
        if not self._check_perm("COMMENT", "add a comment"):
            return

        utils.safe_service_connect(self.instance.add_comment, self.page_id,
                                   comment)
Exemple #13
0
    def render_message(self, message_id):
        """render_message Retrieve and render a message by ID#

        Gmail mnessages are stored in a JSON-like structure that must be
        parsed out to get the tasty nougat center.

        Parameters
        ----------
        message_id : `str`
            The ['id'] field of an entry in self.message_list

        Returns
        -------
        `dict`
            Dictionary containing the subject, sender, date, and body of
            the message.
        """
        try:
            # Get the message, then start parsing (API: users.messages.get)
            results = utils.safe_service_connect(
                self.service.users().messages().get(userId="me", id=message_id).execute
            )
            payload = results["payload"]
            headers = payload["headers"]

        # If exception, print message and return empty values
        except (HttpError, ConnectionError) as error:
            warnings.warn(
                f"An error occurred within GetMessages.render_message():\n{error}"
            )
            payload = None

        # Return empty dictionary if unsuccessful in connecting
        if not payload:
            return dict(subject="", sender="", date="", body="")

        # Look for Subject and Sender Email in the headers
        for d in headers:
            if d["name"] == "Subject":
                subject = d["value"]
            if d["name"] == "From":
                sender = d["value"]
            if d["name"] == "Date":
                date = d["value"]

        # The Body of the message is in Encrypted format -- decode it.
        #  Get the data and decode it with base 64 decoder.
        data = payload["body"]["data"]
        data = data.replace("-", "+").replace("_", "/")
        decoded_data = base64.b64decode(data)

        # `decoded_data` is in lxml format; parse with BeautifulSoup
        body = BeautifulSoup(decoded_data, "lxml").body()
        body = body[0].text

        # Return a dictionary with the plain-text components of this message
        return dict(subject=subject, sender=sender, date=date, body=body)
    def delete_attachment(self, filename):
        """delete_attachment Delete an attachment from this page

        Wrapper for the Confluence method delete_attachment() that includes the
        page ID of this object and is wrapped in utils.safe_service_connect().

        An attahment may be deleted using either the filename, or by passing
        `None` or "" to the filename and specifying the attachment_id.

        Parameters
        ----------
        filename : `str`
            Filename of the attachment to delete
        """
        if not self._check_perm("REMOVEATTACHMENT", "remove an attachment"):
            return

        utils.safe_service_connect(self.instance.delete_attachment,
                                   self.page_id, filename)
Exemple #15
0
    def update_msg_labels(self, message_id, add_labels=None, remove_labels=None):
        """update_msg_labels Update the labels for a message by ID#

        _extended_summary_

        Parameters
        ----------
        message_id : `str`
            The ['id'] field of an entry in self.message_list
        add_labels : `list`, optional
            The list of label IDs to add to this message [Default: None]
        remove_labels : `list`, optional
            The list of label IDs to remove from this message [Default: None]

        Returns
        -------
        `Any`
            Uh, the Message object from Gmail... probably just return nothing?
        """
        if not add_labels and not remove_labels:
            print("No labels to change.")
            return None

        # Convert Label Names to Label IDs
        add_label_ids, remove_label_ids = [], []
        if add_labels:
            for label in add_labels:
                add_label_ids.append(self._lableId_from_labelName(label))
        if remove_labels:
            for label in remove_labels:
                remove_label_ids.append(self._lableId_from_labelName(label))

        # Build the label dictionary to send to Gmail
        body = {}
        if add_label_ids:
            body["addLabelIds"] = add_label_ids
        if remove_label_ids:
            body["removeLabelIds"] = remove_label_ids

        try:
            # Modify message lables (API: users.messages.modify)
            return utils.safe_service_connect(
                self.service.users()
                .messages()
                .modify(userId="me", id=message_id, body=body)
                .execute
            )
        # If exception, print message
        except (HttpError, ConnectionError) as error:
            warnings.warn(
                f"An error occurred within GetMessages.update_msg_labels():\n{error}"
            )
        # If unsuccessful in connecting, return None
        return None
    def update_contents(self, body):
        """update_contents Update the contents of the Confluence page

        Update the page by replacing the existing content with new.  The idea
        for this method is to be used in concert with `get_page_contents` to
        obtain a page, modify it, then replace it.

        Parameters
        ----------
        body : `str`
            The new page contents to upload to Confluence.

        Returns
        -------
        _type_
            _description_
        """
        if not self._check_perm("EDITSPACE", "update a page"):
            return

        utils.safe_service_connect(self.instance.update_page, self.page_id,
                                   self.title, body)
    def get_page_contents(self):
        """get_page_contents Retrieve the page contents in HTML-ish format

        Either for curiosity or for modification, get the page contents, which
        live in the `body.storage` portion of the `get_page_by_id` response.

        Returns
        -------
        `str`
            The HTML-ish body of the confluence page.
        """
        contents = utils.safe_service_connect(self.instance.get_page_by_id,
                                              self.page_id,
                                              expand="body.storage")
        # Extract the contents from the return object
        return contents["body"]["storage"]["value"]
Exemple #18
0
    def _lableId_from_labelName(self, name):
        """_lableId_from_labelName Get the Label ID from the Label Name

        _extended_summary_

        Parameters
        ----------
        name : `str`
            Label name

        Returns
        -------
        `str`
            Label ID
        """
        if not self.service:
            return None

        # Only do this once
        if not self.label_list:
            # Get the list of labels for the "me" account (API: users.labels.list)
            try:
                results = utils.safe_service_connect(
                    self.service.users().labels().list(userId="me").execute
                )
                self.label_list = results.get("labels", [])
            except (HttpError, ConnectionError) as error:
                warnings.warn(
                    f"An error occurred within GetMessages._labelId_from_labelName():\n{error}"
                )
                self.label_list = []

        # If there are no labels, return None
        if not self.label_list:
            print("Whoops, no labels found.")
            return None

        label_id = None
        # Go through the labels, and return the ID matching the name
        for label in self.label_list:
            if label["name"] == name:
                label_id = label["id"]

        return label_id
    def get_page_attachments(self, limit=200):
        """get_page_attachments _summary_

        Return a list of page attachment IDs, up to `limit` in length.

        Parameters
        ----------
        limit : `int`, optional
            The number of attachments to return [Default: 200]

        Returns
        -------
        `list`
            List of Confluence attachment IDs
        """
        return utils.safe_service_connect(
            self.instance.get_attachments_from_content,
            self.page_id,
            limit=limit)
Exemple #20
0
    -------
    `googleapiclient.discovery.Resource`
        The GMail API service object for consumption by other routines
    """
    # Read in the credential token
    creds = None
    if os.path.exists(token_fn := utils.Paths.gmail_token):
        creds = Credentials.from_authorized_user_file(token_fn, SCOPES)

    # If there are no (valid) credentials available...
    if not creds or not creds.valid:

        # If just expired, refresh and move on
        if creds and creds.expired and creds.refresh_token:
            try:
                utils.safe_service_connect(creds.refresh, Request())
            except (HttpError, ConnectionError) as error:
                warnings.warn(f"An error occurred within setup_gmail():\n{error}")

        # If running in `interactive`, lauch browser to log in
        elif interactive:
            flow = InstalledAppFlow.from_client_secrets_file(
                utils.Paths.gmail_creds, SCOPES
            )
            creds = flow.run_local_server(port=0)

        # Otherwise, raise an exception and specify to run interactively
        else:
            raise ValueError(
                "\nNo Gmail credentials found.  You may need to run:\n"
                "j5_install_conf\n"