Avoid logging into Listmonk for Campaign Previews. Use this hack instead

The Hurdle

We have set up a system of doing a mini-research every week. Each one of the team members takes up a mini-unknown area.

We learn about the area, create a solution, and if it's good then transform it into a blog so that others may benefit from it.

We wanted to keep the data with us. So, We set up a blogging platform using Ghost Blog

As the quality of the content got a little better people started recognizing and subscribing to our blog.

Once we had 50+ subscribers it was evident that we needed to set up a newsletter system.

Ghost came with a newsletter setup, but we had to pay for MailChimp to use that hence we set up a self-hosted email manager Listmonk on our own.

We had to spend a week coming up with a sync system for migrating subscribers.

We set up a sync system and migrated all the Ghost subscribers to Listmonk subscribers so that we can make an automated scheduled newsletter every week.

Scheduling Newsletter

We wanted a scheduled newsletter for every Monday, as the writing would be finished by Sunday.

We thought of making it automated so it does not depend on manually sending it and made use of a Cron job for automation.

We have one more newsletter called 365 Reasons which will be sent throughout the year.

1 powerful reason a day nudging you to read
so that you can read more, and level up in life.

Sent throughout the year. Absolutely FREE.

The script is fully automated, but we prefer to do a quality check before a campaign sends emails to dozens of recipients.

We should not send spam or corrupted emails, it is wasting a person's time. Logging in every time would consume our precious time.

Since this is a daily chore, we wanted to make it easy on ourselves

Listmonk didn't have an integrated solution to this, so we came up with our idea i.e. Sending a snapshot of the HTML before sending the newsletter.

How I Implemented a Preview

Listmonk doesn't come with template, so we are using Jinja template for rendering the post, we fetch the last week's posts from our blog. Render the post using a template.

In the end, we have an HTML Posted to the Listmonk server. We fetch the scheduled Email and take a screenshot of it.

I used Pyppeteer library to get a screenshot of the HTML before sending it.

Getting Preview

I have a function that takes the Campaign ID as input and sends a screenshot to the Discord server.

In send_discord_preview_notification function, it gets an HTML response and and inputted into send_discord_notificationfunction.

If there is an error in getting a response then the log_message function will send an error message to our Discord server.

LIST_MONK_URL = lm.example.com # Give Your LM Url 
def send_discord_preview_notification(id):  # This will send Screenshot of HTML file to discord server
    url = f"{LIST_MONK_URL}/api/campaigns/{id}/preview"
    try:
        response = requests.get(url, headers=lm_headers)
        response.raise_for_status()  # This will raise an HTTP error if the response was an HTTP error
    except requests.exceptions.RequestException as e:
        send_discord_notification(f"An error occurred: {e}")
    else:
        if response.status_code == 200:
            send_screenshot_file(response)  # This will initiate Screenshot sending feature
        else:
            log_message(f"Failed to fetch data. Status code: {response.status_code}")

Temporary File Handling

The send_screenshot_file function takes the response as input and sends a screenshot to discord server.

I used the Temporary directory for storing the temp file generated while taking a Screenshot of the campaign.

The response is written inside temp_file_path in .html format in the temporary directory.

Initiate Asynchronous operation to take a screenshot of temp.html file by screenshot function and save a screenshot in screenshot_path.

The Screenshot saved in screenshot_path is posted to the discord server.

import tempfile
WEBHOOK_CHANNEL_URL = "https://discord.com/api/webhooks/121fwebwefuik9886/Zffwuica" 
# Give Your Webhook 
def send_screenshot_file(response):
    with tempfile.TemporaryDirectory() as tmpdirname:
        with tempfile.NamedTemporaryFile(dir=tmpdirname, delete=False, suffix=".html") as temp_file:
            temp_file.write(response.text.encode())
            temp_file.close()
            temp_file_path = (f"file://{temp_file.name}") # Construct the file URL for Pyppeteer
            screenshot_path = f"{tmpdirname}/Screenshot.png"
            asyncio.get_event_loop().run_until_complete(screenshot(screenshot_path, temp_file_path))
            with open(screenshot_path, "rb") as img:
                response = requests.post(NEWSLETTER_WEBHOOK_CHANNEL_URL, files={"file": img})
            print(response.status_code)

Screenshot

I tried many other solutions but it didn't go well some of them were sending previews before images were loaded and others only dent half of the preview.

Finally, came up with using Pyppeteer library. This Library didn't give any problems in the preview.

This function will load HTML in the browser take a screenshot of it and save it in screenshot_path.

import asyncio
from pyppeteer import launch
async def screenshot(screenshot_path, temp_file_path):
    browser = await launch()
    page = await browser.newPage()
    await page.goto(temp_file_path)
    await page.screenshot({"path": screenshot_path, "fullPage": True})
    await browser.close()

Conclusion

This will save us a lot of time which is skipping of logging in and searching for the campaign to preview.

You can use the snippits above for saving a little time of your day.

Stay tuned to our blog for upcoming insights on implementing a newsletter system using Listmonk. Subscribe now to stay ahead of the curve.

FeedZap: Read 2X Books This Year

FeedZap helps you consume your books through a healthy, snackable feed, so that you can read more with less time, effort and energy.