Example #1
0
def create(addr: str) -> requests.Response:
    """Add a subscription email address."""
    return requests.post(
        f"https://api.mailgun.net/v3/lists/{mailing_list()}/members",
        auth=("api", get_secret("MG_API_KEY")),
        data={"upsert": True, "subscribed": True, "address": addr},
    )
Example #2
0
def get_file_for_date(full_date: datetime) -> Optional[str]:
    """Determine if an archive file for a date exists."""
    date_iso = helpers.format_datetime_ymd(full_date)
    save_dir = Path(get_secret("DOWNLOADS_DIR")).resolve()
    file_name = f"{FILE_NAME_BASE}{date_iso}{FILE_NAME_EXT}"
    full_path = save_dir / file_name

    # If the file exists, return a Path to it
    if full_path.exists():
        return file_name
    return None
Example #3
0
def validate(addr: str) -> bool:
    """Validate an email address using the Mailgun Email Validation API."""
    # Assume the address is valid if we can't send out emails
    if not get_config("ENABLE_EMAIL_SENDING"):
        return True

    r = requests.get(
        "https://api.mailgun.net/v4/address/validate",
        auth=("api", get_secret("MG_API_KEY")),
        params={"address": addr},
    ).json()

    # The address can be added if it's marked as deliverable
    return r["result"] == "deliverable"
Example #4
0
def send(email: Dict[str, str]) -> bool:
    """Send out a completed email."""
    # If email sending is not configured, just pretend the email
    # sent out correctly instead of making the caller handle the special case
    if not current_app.config["ENABLE_EMAIL_SENDING"]:
        return True

    # Attempt to send out the email
    r = requests.post(
        f'https://api.mailgun.net/v3/{get_secret("MG_DOMAIN")}/messages',
        auth=("api", get_secret("MG_API_KEY")),
        data=email,
    )
    r.raise_for_status()
    return True
Example #5
0
def download(prompt_id: str, url: str) -> dict[str, str]:
    """Download a Tweet's media."""
    # Generate a random file name for the download
    original_f_name = original_name(url)
    temp_f_name = "{name}{ext}".format(name=secrets.token_hex(12),
                                       ext=PurePath(original_f_name).suffix)

    # Download the media to a temp directory
    r = requests.get(url)
    dl_path = Path(get_secret("IMAGES_DIR_TEMP")).resolve() / temp_f_name
    dl_path.write_bytes(r.content)

    # Return the original and temp file name
    return {
        "original": original_f_name,
        "temp": temp_f_name,
        "final": saved_name(prompt_id, url),
    }
Example #6
0
def make() -> bool:
    """Generate a new Prompt archive spreadsheet."""
    # Check that we have permission to write to the save directory
    save_dir = Path(get_secret("DOWNLOADS_DIR")).resolve()
    try:
        temp_file = save_dir / "perm.temp"
        temp_file.write_text("")
        temp_file.unlink()
    except PermissionError:
        return False

    # Set up all date values we need
    archive_years = database.prompt.get_years()
    archive_range = prompt_date_range()
    oldest_date = helpers.format_datetime_pretty(archive_range["oldest"])
    newest_date = helpers.format_datetime_pretty(archive_range["newest"])
    today = datetime.now()
    today_iso = helpers.format_datetime_ymd(today)
    today_pretty = helpers.format_datetime_pretty(today)

    # Put together the archive's file name
    file_name = f"{FILE_NAME_BASE}{today_iso}{FILE_NAME_EXT}"
    full_save_path = save_dir / file_name

    # Create a new spreadsheet file
    workbook_config = {
        "constant_memory": True,
        "default_date_format": "yyyy-mm-dd"
    }
    with xlsxwriter.Workbook(full_save_path, workbook_config) as workbook:
        # Create a bold text formatter
        bolded_text = workbook.add_format()
        bolded_text.set_bold()

        # Start by creating a page with basic file generation info
        worksheet = workbook.add_worksheet("Info")
        worksheet.write(
            0,
            0,
            f"#vss365 prompt archive from {oldest_date} to {newest_date}",
        )
        worksheet.write(1, 0, "Sorted by prompt in alphabetical order")
        worksheet.write(2, 0, f"Generated on {today_pretty}")
        worksheet.write_url(3, 0, "https://vss365today.com")

        # Group each year's prompts in their own sheet
        for year in archive_years:
            worksheet = workbook.add_worksheet(str(year))

            # Set the column widths
            widths = get_column_widths(int(year))
            worksheet.set_column(0, 0, 10)
            worksheet.set_column(1, 1, widths.longest_word)
            worksheet.set_column(2, 2, widths.longest_handle)
            worksheet.set_column(3, 3, widths.longest_url)

            # Write the headings
            worksheet.write(0, 0, "Date", bolded_text)
            worksheet.write(0, 1, "Prompt", bolded_text)
            worksheet.write(0, 2, "Host", bolded_text)
            worksheet.write(0, 3, "URL", bolded_text)

            # Get the prompt archive for the current year
            for row, prompt in enumerate(get(int(year))):
                # Rows are zero-indexed, meaning we need to increment
                # so we don't clobber the headings
                row += 1

                # Write all the data
                worksheet.write_datetime(row, 0, prompt.date)
                worksheet.write(row, 1, prompt.word)
                worksheet.write(row, 2, prompt.writer_handle)
                url = Prompt.make_url(prompt.writer_handle, prompt.tweet_id)
                worksheet.write_url(row, 3, url)
    return True
Example #7
0
def move(details: dict) -> bool:
    """Move a media file from the temporary directory to final location."""
    current_path = Path(get_secret("IMAGES_DIR_TEMP")) / details["temp"]
    final_path = Path(get_secret("IMAGES_DIR")) / details["final"]
    current_path.replace(final_path)
    return final_path.is_file()
Example #8
0
def delete(prompt_id: str) -> bool:
    """Delete a media file."""
    f_name = sorted(Path(get_secret("IMAGES_DIR")).glob(f"{prompt_id}*"))
    if len(f_name) == 1:
        f_name[0].unlink()
    return True
Example #9
0
def delete(addr: str) -> requests.Response:
    """Remove a subscription email address."""
    return requests.delete(
        f"https://api.mailgun.net/v3/lists/{mailing_list()}/members/{addr}",
        auth=("api", get_secret("MG_API_KEY")),
    )
Example #10
0
def twitter_v2_api() -> tweepy.Client:
    """Connect to Twitter API v2 using a Bearer token."""
    return tweepy.Client(bearer_token=get_secret("TWITTER_BEARER"))