Last active
March 6, 2024 20:23
-
-
Save bradmontgomery/9166407291eb697f2ef5825103d85be7 to your computer and use it in GitHub Desktop.
Quick & dirty hack to list a user's gists, sorted by stars
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
""" | |
A quick & dirty hack to list all of your Gists sorted by stars. | |
This script lists your gists, the number of stars, the number of comments, | |
a truncated description, and the url for the gist. | |
NOTE: This only returns public gists. If you want to see (your own) private gists, | |
you can provide a personal access token. | |
""" | |
import click | |
import requests | |
import sys | |
from github3.github import GitHub | |
from html.parser import HTMLParser | |
from tabulate import tabulate | |
class StarParser(HTMLParser): | |
"""Parse the HTML document for a github gist looking for the number of | |
times a gist has been starred. | |
""" | |
stars = 0 # Numeric value of the "Star" button. | |
# Flags that need to be active before we can try to grab the "DATA" (stored in both title & content) | |
# <span class="Button-content"> | |
# <span class="Button-visual">...</span> | |
# <span class="Button-label">Star</span> <----- Look for this. | |
# <span title="1234" class="Counter">1234</span> <----- Save this. | |
# </span> | |
in_button = False # Flag if the parser is looking _in_ a button | |
is_star = False # Was the body of the preceding span == "Star"? | |
def handle_starttag(self, tag, attrs): | |
if tag == "span" and ("class", "Button-label") in attrs: # We're _inside_ a button | |
self.in_button = True | |
if tag == "span" and "title" in [k for k, _ in attrs]: | |
self.has_title = True | |
if self.in_button and self.has_title: | |
self._tag = tag | |
self._attrs = attrs | |
if tag != "span": | |
self.in_button = False | |
self.has_title = False | |
def handle_data(self, data): | |
# We already found the "Star" button | |
if self.in_button and self.is_star and data and data.strip().isnumeric(): | |
try: | |
self.stars = int(data.strip()) | |
except ValueError: | |
pass | |
# Reset our flags | |
self.in_button = False | |
self.is_star = False | |
# Looking for the "Star" content... | |
if self.in_button and data and data.strip() == "Star": | |
self.is_star = True # next element is the star count. | |
def _trunc(description, chars=50): | |
"""Truncate a description to `chars` characters.""" | |
if description: | |
return description[:chars] | |
return "" | |
def _get_stars(url): | |
"""Scrape the stars number from each gist url.""" | |
parser = StarParser() | |
resp = requests.get(url) | |
stars = 0 | |
if resp.status_code == 200: | |
parser.feed(resp.content.decode("utf8")) | |
return parser.stars | |
return stars | |
def _sorted_by_stars(username, token=None): | |
"""Sort the given user's public gists by the number of starts""" | |
gh = GitHub(token=token) | |
results = [] | |
for gist in gh.gists_by(username): | |
results.append( | |
[ | |
_get_stars(gist.html_url), | |
gist.comments_count, | |
_trunc(gist.description), | |
gist.html_url, | |
"Public" if gist.public else "Private", | |
] | |
) | |
results = sorted(results, reverse=True) | |
print(tabulate(results, headers=["Stars", "Comments", "Description", "Link", "Visibility"])) | |
@click.command | |
@click.argument("username") | |
@click.option("-t", "--token", default=None, help="Optional github personal access token") | |
def main(username, token): | |
print(f"Listing gists for {username}") | |
_sorted_by_stars(username, token) | |
if __name__ == "__main__": | |
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
github3.py==4.0.1 | |
requests==2.31.0 | |
tabulate==0.9.0 | |
click==8.1.7 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment