Ejemplo n.º 1
0
    def describe_system(self):
        """collect metrics about the user system, including type, name
           release, version, encoding, and distribution
        """
        try:
            dist = self.get_linux_distribution()
        except Exception as exc:
            bot.warning("Failed to get distribution information: %s" % exc)
            dist = tuple()

        return {
            "type":
            os.name,
            "name":
            platform.system(),
            "release":
            platform.release(),
            "version":
            platform.version(),
            "distribution":
            " ".join([
                _t2s(dist),
                _t2s(platform.mac_ver()),
                _t2s(platform.win32_ver())
            ]).rstrip(),
            "encoding":
            self.get_encoding_info(),
        }
Ejemplo n.º 2
0
def download(url, file_name, headers=None, show_progress=True):
    """stream to a temporary file, rename on successful completion

        Parameters
        ==========
        file_name: the file name to stream to
        url: the url to stream from
        headers: additional headers to add
    """

    fd, tmp_file = tempfile.mkstemp(prefix=("%s.tmp." % file_name))
    os.close(fd)

    if DISABLE_SSL_CHECK is True:
        bot.warning("Verify of certificates disabled! ::TESTING USE ONLY::")

    verify = not DISABLE_SSL_CHECK

    # Does the url being requested exist?
    if requests.head(url, verify=verify).status_code in [200, 401]:
        response = stream(url, headers=headers, stream_to=tmp_file)

        if isinstance(response, HTTPError):
            bot.error("Error downloading %s, exiting." % url)
            sys.exit(1)
        shutil.move(tmp_file, file_name)
    else:
        bot.error("Invalid url or permissions %s" % url)
    return file_name
Ejemplo n.º 3
0
 def run_headless(self, positionals=None, **kwargs):
     """run a headless helper procedure, meaning that the title, body,
        and other content must be provided to the function. Command line
        arguments such a a GitHub repository or discourse board would be
        provided as positionals, and everything else is passed as kwargs.
     """
     bot.warning("run_headless must be implemented by the Helper class.")
Ejemplo n.º 4
0
def create_post(self, title, body, board, category, username):
    """create a Discourse post, given a title, body, board, and token.

       Parameters
       ==========
       title: the issue title
       body: the issue body
       board: the discourse board to post to

    """

    category_url = "%s/categories.json" % board
    response = requests.get(category_url)

    if response.status_code != 200:
        print("Error with retrieving %s" % category_url)
        sys.exit(1)

    # Get a list of all categories
    categories = response.json()["category_list"]["categories"]
    categories = {c["name"]: c["id"] for c in categories}

    # And if not valid, warn the user
    if category not in categories:
        bot.warning("%s is not valid, will use default" % category)

    category_id = categories.get(category, None)

    headers = {
        "Content-Type": "application/json",
        "User-Api-Client-Id": self.client_id,
        "User-Api-Key": self.token,
    }

    # First get the category ids
    data = {"title": title, "raw": body, "category": category_id}

    response = requests.post("%s/posts.json" % board,
                             headers=headers,
                             data=json.dumps(data))

    if response.status_code in [200, 201, 202]:
        topic = response.json()
        url = "%s/t/%s/%s" % (board, topic["topic_slug"], topic["topic_id"])
        bot.info(url)
        return url

    elif response.status_code == 404:
        bot.error("Cannot post to board, not found. Do you have permission?")
        sys.exit(1)

    else:
        bot.error("Cannot post to board %s" % board)
        bot.error(response.content)
        sys.exit(1)
Ejemplo n.º 5
0
    def load_secrets(self):
        """load secrets, namely the GitHub token, check if required and
           exit if not provided
        """
        self.token = self._get_and_update_setting("HELPME_GITHUB_TOKEN")

        # If the user wants to use a token for the GitHub API
        if not self.token:
            if self.require_token:
                bot.exit("HELPME_GITHUB_TOKEN is required")
            bot.warning("HELPME_GITHUB_TOKEN not found, "
                        "will attempt to open browser manually "
                        "If you have trouble submitting an issue, export it.")
Ejemplo n.º 6
0
def _load_config(configfile, section=None):
    """general function to load and return a configuration given a helper
       name. This function is used for both the user config and global help me
       config files.
    """
    if os.path.exists(configfile):
        config = read_config(configfile)
        if section is not None:
            if section in config:
                return config._sections[section]
            else:
                bot.warning("%s not found in %s" % (section, configfile))
        return config
Ejemplo n.º 7
0
def stream(url, headers, stream_to=None, retry=True):
    """stream is a get that will stream to file_name. Since this is a worker
       task, it differs from the client provided version in that it requires
       headers.
    """

    bot.debug("GET %s" % url)

    if DISABLE_SSL_CHECK is True:
        bot.warning("Verify of certificates disabled! ::TESTING USE ONLY::")

    # Ensure headers are present, update if not
    response = requests.get(
        url, headers=headers, verify=not DISABLE_SSL_CHECK, stream=True
    )

    # Deal with token if necessary
    if response.status_code == 401 and retry is True:
        headers = update_token(response, headers)
        return stream(url, headers, stream_to, retry=False)

    if response.status_code == 200:

        # Keep user updated with Progress Bar
        content_size = None
        if "Content-Length" in response.headers:
            progress = 0
            content_size = int(response.headers["Content-Length"])
            bot.show_progress(progress, content_size, length=35)

        chunk_size = 1 << 20
        with open(stream_to, "wb") as filey:
            for chunk in response.iter_content(chunk_size=chunk_size):
                filey.write(chunk)
                if content_size is not None:
                    progress += chunk_size
                    bot.show_progress(
                        iteration=progress,
                        total=content_size,
                        length=35,
                        carriage_return=False,
                    )

        # Newline to finish download
        sys.stdout.write("\n")

        return stream_to

    bot.error("Problem with stream, response %s" % (response.status_code))
    sys.exit(1)
Ejemplo n.º 8
0
def upload_asciinema(filename):
    """a wrapper around generation of an asciinema.api.Api to call the 
       upload command given an already existing asciinema file. 

       Parameters
       ==========
       filename: the asciinema file to upload, can be generated with 
                 function record_asciinema in record.py

    """
    if os.path.exists(filename):

        try:
            from asciinema.commands.upload import UploadCommand
            import asciinema.config as aconfig
            from asciinema.api import Api
        except:
            bot.exit(
                "The asciinema module is required to submit "
                "an asciinema recording. Try pip install helpme[asciinema]"
            )

        # Load the API class

        cfg = aconfig.load()
        api = Api(cfg.api_url, os.environ.get("USER"), cfg.install_id)

        # Perform the upload, return the url

        uploader = UploadCommand(api, filename)

        try:
            url, warn = uploader.api.upload_asciicast(filename)
            if warn:
                uploader.print_warning(warn)

            # Extract just the url, if provided (always is https)
            if url:
                match = re.search("https://.+", url)
                if match:
                    url = match.group()
            return url

        except:
            bot.error("Problem with upload, skipping")

    else:
        bot.warning("Cannot find %s, skipping submission." % filename)
Ejemplo n.º 9
0
    def record_asciinema(self):
        """record an asciinema from the user session, saving the file to
           a temporary file and showing the user so if he/she needs to do it
           again, the file can be provided. The flow of events below makes
           the following checks:

           1. The user confirms it is ok to record
           2. The record_asciinema setting is present and True in the config
           3. An asciinema file path has not been provided by the user

        """

        # If the user already provided a file, we don't need to ask again
        if "record_asciinema" not in self.data:

            if confirm_prompt("Would you like to send a terminal recording?"):

                try:
                    from helpme.action.record import record_asciinema

                    record = self.config.getboolean(self.name,
                                                    "record_asciinema")
                    filename = record_asciinema()
                    self.data["record_asciinema"] = filename

                    message = ("""If you need to run helpme again you can give
                    the path to this file with  --asciinema %s""" % filename)

                    bot.custom(prefix="Asciinema ",
                               message=message,
                               color="CYAN")

                except NoOptionError:
                    bot.warning("Cannot record asciinema, skipping.")

                except:
                    bot.exit(
                        "The asciinema module is required to submit "
                        "an asciinema recording. Try pip install helpme[asciinema]"
                    )
Ejemplo n.º 10
0
def call(
    url, func, data=None, headers=None, return_json=True, stream=False, retry=True
):

    """call will issue the call, and issue a refresh token
    given a 401 response, and if the client has a _update_token function

    Parameters
    ==========
    func: the function (eg, post, get) to call
    url: the url to send file to
    headers: headers for the request
    data: additional data to add to the request
    return_json: return json if successful
    """

    if DISABLE_SSL_CHECK is True:
        bot.warning("Verify of certificates disabled! ::TESTING USE ONLY::")

    if data is not None:
        if not isinstance(data, dict):
            data = json.dumps(data)

    response = func(
        url=url, headers=headers, data=data, verify=not DISABLE_SSL_CHECK, stream=stream
    )

    # Errored response, try again with refresh
    if response.status_code in [500, 502]:
        bot.error("Beep boop! %s: %s" % (response.reason, response.status_code))
        sys.exit(1)

    # Errored response, try again with refresh
    if response.status_code == 404:
        bot.error("Beep boop! %s: %s" % (response.reason, response.status_code))
        sys.exit(1)

    # Errored response, try again with refresh
    if response.status_code == 401:

        # If client has method to update token, try it once
        if retry is True:

            # A result of None indicates no update to the call
            headers = update_token(response, headers)
            return call(
                url,
                func,
                data=data,
                headers=headers,
                return_json=return_json,
                stream=stream,
                retry=False,
            )

        bot.error(
            "Your credentials are expired! %s: %s"
            % (response.reason, response.status_code)
        )
        sys.exit(1)

    elif response.status_code == 200:

        if return_json:

            try:
                response = response.json()
            except ValueError:
                bot.error("The server returned a malformed response.")
                sys.exit(1)

    return response