Tuesday, June 17, 2025

Posting ACARS Messages Online



After all the work done to set up a radio monitor to scan the ACARS frequenices, figuring out what kind of messages I wanted to see, and finally building a way to easily view the messages, the final  part of the project is to now post these messages up somewhere where I can view them almost anywhere.

 As mentioned in the previous post, I had looked into building a feed to the usual social media applications, but usually these types of feeds usually required the use of API connections to make things work. While this is pretty common within the IT world where I spend my working hours, I also realize that a good part of the world may not be familiar with the voodoo that define electronic data interchange standards, so I wanted to develop a solution that would be pretty simple to implement by someone who wasn't a tech guru. 

After some experiementing, I settled on a solution that would post my daily ACARS feed up to a fairly simple Blogger web site. The main reason for using Blogger is that it's a platform that is built to allow people to post to a personal webpage via a relativley simple user interface.  Blogger also has the ability to customize the look and feel of your webpage, so you can be creative and have your webpage suit your personal taste.  The third (and probably the most important) reason I chose Blogger was that it was free. 

Since Blogger utilizes a graphical interface for creating posts, I thought it might be interesting to try and create a solution that minics a live person creating a post. Granted, this would probably be seen as sacraligous by my IT colleagues to consider this sort of solution, but like I said earlier, I wanted to build something that a "non-techy" person might be able to build with a minimal amount of fuss.    

 With those goals in mind, I started work by creating a Blogger site called "ACARS Radio Log" <Click To Visit Site> and after doing some tinkering on my site's look and feel, I had somethng that allowed me to easily see the current messages.

With the website set up, I next looked into how to build the process of loading up ACARS messages to the site.

Obviously I wanted to look at some sort of programmed solution and since I've had pretty good luck with Python so far I wanted to still some how be able to use it for the solution's foundation. 

In sketching out a logic flow, I can up with this high level process:

  • Open a browser (I chose Chrome as the browser for my solution)
  • Log onto the Google account (since Blogger is owned by Google)
  • Open the blogger session for my ACARS Log site and click on the New Post button
  • Set the Edit window that opens up to "HTML" mode
  • Copy the contents of the "blog_ready.html" file that was created in the previous process into the Blogger Edit window
  • Save and Post the Blog Post.    

An Interactive Session

To make this work, I needed to program something that was going to work in interactive mode, which as it suggests, means that the program needs to "interact" with the computer's GUI environment, which is a bit different for Python program, which usually likes to work behnd the scenes. 

The one thing that I like the most about Python is that there seems to be a really extensive library of companion tools that seems to give it the ability do almost anything. After some research, I came across a plug in called Playwright, which is a general purpose browser automation tool. 

Doing some experimenting with Playwright, I found that it would do a great job of opening up a browser and logging onto my Blogger page. However it was having troubles navigating the editor on Blogger. Doing some investigation showed that Playwright was looking for specific html code snippets within the Blogger page itself that didn't exist in the current Blogger site. What this told me was that Blogger would occasionally make some internal changes to their site, which even if I could get it to evetually work with Playwright, it was likely a matter of time before it broke again. 

To get around that I needed to find a method that avoided the need for looking at the internal programming of the site.  After some poking around, I decided I needed to resort to a good old fashion key logger. 

Key Logging

Again, looking at the Python libraries, I did find a plugin that would play back recorded key stokes. I ended up using a plugin called Pynput. This tool will allow you to record and playback any keystrokes on a computer, which is then stored as a json file, making editting of your keystrokes pretty easy if you need to modify anything. 

The first step was to get my Blogger session to the point where I was starting to have problems with Playwright. To create the keylogger file, I needed to create a "throw away" Python to create the log:

# keylogger.py
from pynput import keyboard
import json

keystrokes = []

def on_press(key):
    try:
        keystrokes.append(key.char)
    except AttributeError:
        keystrokes.append(str(key))

def on_release(key):
    if key == keyboard.Key.esc:
        # Save to file on ESC key press
        with open("keystrokes+2.json", "w") as f:
            json.dump(keystrokes, f)
        print("Keystrokes saved. Exiting...")
        return False  # Stop listener

with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
    print("Recording... Press ESC to stop.")
    listener.join()

Executing this program turns the recorder on and I can start recording keystrokes (I noticed that it won't record any mouse activity). After I finished my keystrokes, I simply turned the recorder off by pressing the Escape key. 

After recording, I got this as a json file:

["Key.tab", "Key.tab", "Key.tab", "Key.tab", "Key.tab", "Key.enter", "Key.down", "Key.up", "Key.enter"]

After putting together some Pynput playback code, I gave it a quick test 


It certainly will do what I want it to do. 

Posting To The Blog

Now that I've sorted out the mechanics how things should work, I threw together a small Python program that follows the logic flow that I've highlighted earlier

import os
import time
from playwright.sync_api import sync_playwright
from pynput.keyboard import Controller, Key
import json
import pyperclip


# === CONFIGURATION ===
BLOGGER_URL = "https://www.blogger.com"
HTML_FILE_PATH = "blog_ready.html"
PASTE_DELAY = 1.0

# === Credentials ===
EMAIL = "<My Google Account>"
PASSWORD = "<My Password>"

if not EMAIL or not PASSWORD:
    raise ValueError("Missing EMAIL or PASSWORD")

# === Load HTML content ===
with open(HTML_FILE_PATH, 'r', encoding='utf-8') as file:
    html_content = file.read()

# === Copy to clipboard ===
pyperclip.copy(html_content)

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False, args=[
        '--disable-blink-features=AutomationControlled',
        '--incognito',
        '--disable-extensions',
        '--start-maximized'
    ])
    context = browser.new_context()
    page = context.new_page()

    # Stealth
    page.add_init_script("""
        Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
    """)

    # Step 1: Google login
    page.goto("https://accounts.google.com/")
    page.wait_for_selector("input[type='email']", timeout=10000)
    page.fill("input[type='email']", EMAIL)
    page.click("#identifierNext")

    page.wait_for_selector("input[type='password']", timeout=10000)
    page.fill("input[type='password']", PASSWORD)
    page.click("#passwordNext")

    page.wait_for_timeout(5000)  # Let login settle

    # Step 2: Go to Blogger dashboard
    page.goto(BLOGGER_URL)
    page.wait_for_load_state("load", timeout=15000)
    page.wait_for_timeout(5000)

    # Click New Post
    print("Clicking NEW POST button...")
    try:
        new_post_button = page.get_by_role("button", name="New Post").first
        new_post_button.wait_for(state="visible", timeout=10000)
        new_post_button.click()
    except Exception as e:
        print(f"Failed to click NEW POST button: {e}")
    # Do not close the browser; just wait for user input
        input("Press Enter to exit and close the browser...")
        exit(1)

    keyboard_controller = Controller()

    # Load keystrokes from file
    with open("keystrokes_1.json", "r") as f:
        keystrokes = json.load(f)
 
    print("Replaying keystrokes in 3 seconds...")
    time.sleep(3)

    for key in keystrokes:
        if key.startswith("Key."):
            # Convert string back to actual Key
            try:
                k = getattr(Key, key.split(".")[1])
                keyboard_controller.press(k)
                keyboard_controller.release(k)
            except AttributeError:
                pass  # Unknown key
        else:
            keyboard_controller.press(key)
            keyboard_controller.release(key)
        time.sleep(0.05)  # slight delay to mimic real typing

    # === Wait for user to focus editor ===
    print("📋 HTML content copied to clipboard.")
    print("Switch to the editor window in the next few seconds...")
    time.sleep(PASTE_DELAY)

    # === Paste using keyboard (Ctrl+V) ===
    keyboard = Controller()
    keyboard.press(Key.ctrl)
    keyboard.press('v')
    keyboard.release('v')
    keyboard.release(Key.ctrl)

    print("✅ Content pasted.")
  
     # === Publish post ===
    try:
        print("🚀 Attempting to publish post...")

    # Look for 'Publish' button
        publish_button = page.locator('div[role="button"] span:has-text("Publish")').first
        publish_button.wait_for(state="visible", timeout=10000)
        publish_button.click()
        print("🟡 Clicked Publish button...")
        
    except Exception as e:
        print("❌ Could not publish post:", e)
    

    # Confirm Publish
    
    with open("keystrokes_2.json", "r") as f:
        keystrokes = json.load(f)
 
    print("Replaying keystrokes in 3 seconds...")
    time.sleep(3)

    for key in keystrokes:
        if key.startswith("Key."):
            # Convert string back to actual Key
            try:
                k = getattr(Key, key.split(".")[1])
                keyboard_controller.press(k)
                keyboard_controller.release(k)
            except AttributeError:
                pass  # Unknown key
        else:
            keyboard_controller.press(key)
            keyboard_controller.release(key)
        time.sleep(0.05)  # slight delay to mimic real typing

    print("✅ Confirmed Publish.")

    time.sleep(3)
    browser.close()

The program follows this flow:
  • It first logs onto my Blogger page through the associated Google account. 
  • Next it navigates to the Blogger Editor by simulating the pressing of the New Post button.
  • Once the Editor screen is open, the Keylogger playback navigates the editor by first setting the editor to html mode, and tabbing over to the edit screen. 
  • The html code that has been created in the ACARS_to_htm program is pasted to the Editor
  • The post is then published 

Putting It All Together


Now that I've got a full end to end process to collect, filter and post the ACARS messages, the last step is to put the whole thing together as a consolidated script that will run everything at once.  

To do this I created a script file called Create_post.sh on my ACARS computer:

# Run file_rename.py
python3 file_rename.py

# Check if the first script succeeded
if [ $? -ne 0 ]; then
    echo "file_rename.py failed. Aborting."
    exit 1
fi
# Run ACARS_to_html.py
python3 ACARS_to_html.py
# Check if the second script succeeded
if [ $? -ne 0 ]; then
    echo "ACARS_to_html.py failed."
    exit 1
fi
# Run ACARS_to_html.py
python3 Post_Blog.py
# Check if the second script succeeded
if [ $? -ne 0 ]; then
    echo "Post_Blog.py failed."
    exit 1
fi

echo "All scripts executed successfully."

To finish things up I then set up a crontab schedule to run this script at midnight every day. 

So far it's been running like clockwork everyday, and I love really being able to pop in anytime to check out the activity. 

So that completes the ACARS project. I'll admit that there was probably a bit more software with this one. I promise that the next project will be a bit more hands on! 

No comments:

Post a Comment