PYTHON SCRIPT: JS8 RSS NEWS READER

I started working on a project with my friend Justin in 2022, we were able to put several months in and conducted a lot of experiments.

To learn more about what we have been trying to do: RADENGINEERING.TECH

We haven’t given up on the project, but we both have to make money, to get by, and we don’t have the time to put much more into it …

In order to promote this project, I was asked to build a simple RSS newsreader, operating using JS8 protocol, accessing some of the software’s API and using log files for monitoring. Not a lot of code allowed for a pretty cool experience, where headlines were being sent using CB RADIO, 11M, 27.245 MHZ.

Do with it what you will.

SQL Code (for MySQL): https://planetarystatusreport.com/RSS.TXT

https://youtube.com/watch?v=1nxT5ZXOt-c
#Daniel John Sullivan, CEO, Rad Engineering, 3/23/22

#This is a news agent/script, a prototype
#Service, utilizing JS8 as a proxy agent
#to encode messages and decode from our
#transceiver

#JS8 Callsign for this service - N3W5 or NEWS ...

#1. monitor directed file for requests
#2. respond to new requests
#3. grab RSS feed data

#there are a few python modules that need
#to be installed: maidenhead, feedparser, bs4
# anyascii, and perhaps one or two others
# just be comfortable using pip3 to install
# these other modules / libraries

from __future__ import print_function

import os
import feedparser
import os.path, time
import json
import math
import time
import maidenhead as mh
import urllib.parse as pr
import xml.etree.ElementTree as ET
from bs4 import BeautifulSoup as BS
from requests import get
from os.path import exists
from socket import socket, AF_INET, SOCK_STREAM
from decimal import Decimal
from datetime import datetime, date, timedelta
from anyascii import anyascii
import mysql.connector

### globals ###

DPath = "/home/pi/.local/share/JS8Call/DIRECTED.TXT"

# server/port info for connecting to the JS8 Call application
# while it is running, it can act as a message server/proxy
# for your radio kit ... we are still figuring out the API
# so right now, it's just 'send' capabilities we are using
# - DJS, 3/19/22

JS8Server = "127.0.0.1"
JS8PortNum = 2442

#make sure you open port 2442 prior to opening JS8 application
#ubuntu command: sudo ufw allow 2442
server = (JS8Server, JS8PortNum)

servern = "localhost";
portn = 3306
usern = "DBUSER";
passw = "DBPASSWORD";
dbn = "DBNAME";

#mode 1: just top stories from each feed, sent just once
#mode 2: active engagement mode
#mode 3: just one service

newsServiceM3 = "ONION"

newsMode = 3

bigSleep = 90

### end of globals ###

def from_message(content):
	try:
		return json.loads(content)
	except ValueError:
		return {}

def to_message(typ, value='', params=None):
	if params is None:
		params = {}
	return json.dumps({'type': typ, 'value': value, 'params': params})

class JS8Client(object):

	def __init__(self):
		self.PttON = False
		self.LastPtt = datetime.now()

	def process(self, message):
		typ = message.get('type', '')
		value = message.get('value', '')
		params = message.get('params', {})
		if not typ:
			return
		if typ in ('RX.ACTIVITY',):
			# skip
			return

		if value and typ == "RIG.PTT":
			if value == "on":
				self.PttON = True
				self.LastPtt = datetime.now()
				print("PTT ON")
			if value == "off":
				self.PttON = False
				print("PTT OFF")
				
	def send(self, *args, **kwargs):
		params = kwargs.get('params', {})
		if '_ID' not in params:
			params['_ID'] = '{}'.format(int(time.time()*1000))
			kwargs['params'] = params
		message = to_message(*args, **kwargs)
		self.sock.send((message + '\n').encode()) # remember to send the newline at the end :)

	def GetStationStatus(self):
		self.sock = socket(AF_INET, SOCK_STREAM)
		self.sock.connect(server)
		self.connected = True
		try:			
			self.send("TX.GET_TEXT", "")
			content = self.sock.recv(65500)
			message = json.loads(content)
			
			typ = message.get('type', '')
			value = message.get('value', '')
			
			if typ == "TX.TEXT":
				vt = value.strip()
				if len(vt) < 1:
					return "OPEN"
				else:
					return "CLOSED"
			else:
				return "CLOSED"
		except:
			return "CLOSED"
		finally:
			self.sock.close()

	def SendToJS8(self, JS8Message):
		self.sock = socket(AF_INET, SOCK_STREAM)
		self.sock.connect(server)
		self.connected = True
		try:			
			self.send("TX.SEND_MESSAGE", JS8Message)
			content = self.sock.recv(65500)
			print(content)
		except:
			print("Error sending message to JS8 via API.")
		finally:
			self.sock.close()

	def close(self):
		self.connected = False

#### END OF CLASS DEFINITION FOR JS8 API INTERFACE ####

def GetArt(number):
	# Connect with the MySQL Server
	cnx = mysql.connector.connect(user=usern, database=dbn, password=passw, host=servern, port=portn)
	qry = "select ARTICLE, SOURCE, LINK from RSS where ID = %s" % (number)
	cur = cnx.cursor(buffered=True)
	cur.execute(qry)
	retRes = cur.fetchall()
	cnx.close()
	return retRes[0]

def GetTopHourly(source):
	# Connect with the MySQL Server
	cnx = mysql.connector.connect(user=usern, database=dbn, password=passw, host=servern, port=portn)
	qry = "select ID, TITLE, PUBLISHED, SOURCE, length(ARTICLE) as LOF from RSS where SOURCE = '%s' order by PUBLISHED desc limit 1" % source
	cur = cnx.cursor(buffered=True)
	cur.execute(qry)
	retRes = cur.fetchall()
	cnx.close()
	return retRes

def GetTop(source, number):
	# Connect with the MySQL Server
	cnx = mysql.connector.connect(user=usern, database=dbn, password=passw, host=servern, port=portn)
	qry = "select ID, TITLE, PUBLISHED, SOURCE, length(ARTICLE) as LOF from RSS where SOURCE = '%s' order by PUBLISHED desc limit %s" % (source, number)
	cur = cnx.cursor(buffered=True)
	cur.execute(qry)
	retRes = cur.fetchall()
	cnx.close()
	return retRes

def AlreadySaved(link):
	# Connect with the MySQL Server
	cnx = mysql.connector.connect(user=usern, database=dbn, password=passw, host=servern, port=portn)
	qry = "select ID from RSS where LINK = '" + link + "'"
	cur = cnx.cursor(buffered=True)
	cur.execute(qry)
	cur.fetchall()
	rc = cur.rowcount
	cnx.close()
	if rc > 0:
		return True
	else:
		return False

def SaveRSS(source, title, link, published, article):

	tit = title.replace("'", "''")

	clean_text = anyascii(article)
	
	art = str(clean_text)

	art = art.replace("'", "''")
	
	if len(art) > 5000:
		art = art[0:5000]
	
	cnx = mysql.connector.connect(user=usern, database=dbn, password=passw, host=servern, port=portn)
	
	cur = cnx.cursor()

	qry = """
	INSERT INTO RSS
	(SOURCE, 
	LINK, 
	TITLE, 
	PUBLISHED, 
	ARTICLE) 
	VALUES 
	(%s,%s,%s,%s,%s)
	""" 
	
	val = (source, link, tit, published, art)

	cur.execute(qry, val)

	cnx.commit()
	
	cnx.close()

def GrabRSS(RssURL, SourceName):

	hdrs = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'}

	NewsFeed = feedparser.parse(RssURL)

	for na in NewsFeed.entries:

		try:
			print(na.title)
			print(na.link)
			print(na.published)
			print(na.published_parsed)
		except:
			continue
			
		if AlreadySaved(na.link):
			continue

		print("*************************")

		response = get(na.link, None, headers=hdrs)

		print(na.keys())

		soup = BS(response.content, 'html.parser')
	
		txtChunk = ""

		for data in soup.find_all("p"):
			txtval = data.get_text()
			txtval = txtval.strip()
			txtarr = txtval.split()
		
			if len(txtarr) == 1:
				continue
		
			if "posted" in txtval and ("hours" in txtval or "days" in txtval) and len(txtarr) == 4:
				continue
			
			if txtval == "No Search Results Found":
				continue
			
			if txtval == "Terms of Service":
				continue
		
			if txtval == "Advertise with us":
				continue
			
			if txtval == "Media Inquiries":
				continue

			txtChunk += " " + txtval + "\n"
	
		tyr = na.published_parsed[0]
		tmn = na.published_parsed[1]
		tdy = na.published_parsed[2]
		thr = na.published_parsed[3]
		tmi = na.published_parsed[4]
		tsc = na.published_parsed[5]
	
		ptms = "%s-%s-%s %s:%s:%s" % (tyr, tmn, tdy, thr, tmi, tsc)	
	
		SaveRSS(SourceName, na.title, na.link, ptms, txtChunk.strip())
	
		print(txtChunk.strip())

def debugHere():
	input("Press enter to continue ...")

def clearConsole():
	command = 'clear'
	if os.name in ('nt', 'dos'):  # If Machine is running on Windows, use cls
		command = 'cls'
	os.system(command)

def TopStoriesFrom(news, numof):
	
	s = JS8Client()

	ti = GetTop(news, numof)
	
	for t in ti:
			
		pubarr = str(t[2]).split()	
		pubdte = pubarr[0]
		ct = t[1].replace("''","'")
		# ID, TITLE, PUBLISHED, SOURCE, LOF
		
		lof = int(t[4])

		moreLink = ""
		
		#if lof > 40:
		#	moreLink = "4MORE: N3WZ [ART]: %s" % t[0]	
		
		#headline = "%s HEADLINE [%s] '%s', #%s %s" % ("@ALLCALL", pubdte, ct, t[3], moreLink)  
		
		#headline = headline.strip()
		
		headline = "%s HEADLINE [%s] '%s', #%s" % ("@ALLCALL", pubdte, ct, t[3]) 
		
		print(headline)
		
		while s.GetStationStatus() == "CLOSED":
			print("Waiting to send ..." + str(datetime.now()))
			time.sleep(2)
		s.SendToJS8(headline)
		while s.GetStationStatus() == "CLOSED":
			print("Waiting to send ..." + str(datetime.now()))
			time.sleep(2)
		time.sleep(bigSleep)

def TopStoriesFromAll():
	
	s = JS8Client()

	ti = GetTopHourly("INFOWARS")
	tn = GetTopHourly("NYT")
	tz = GetTopHourly("ZEROHEDGE")
	ty = GetTopHourly("YAHOO")
	tc = GetTopHourly("CNN")
	tb = GetTopHourly("BBC")
	
	tops = []
	
	tops.append(ti[0])
	tops.append(tz[0])
	tops.append(ty[0])
	tops.append(tc[0])
	tops.append(tn[0])
	tops.append(tb[0])

	for t in tops:
		pubarr = str(t[2]).split()	
		pubdte = pubarr[0]
		ct = t[1].replace("''","'")
		
		# ID, TITLE, PUBLISHED, SOURCE, LOF
		
		lof = int(t[4])

		moreLink = ""
		
		if lof > 40:
			moreLink = "4MORE: N3WZ [ART]: %s" % t[0]	
		
		headline = "%s HEADLINE [%s] '%s', #%s %s" % ("@ALLCALL", pubdte, ct, t[3], moreLink)  
		
		print(headline)
		while s.GetStationStatus() == "CLOSED":
			print("Waiting to send ..." + str(datetime.now()))
			time.sleep(2)
		s.SendToJS8(headline)
		while s.GetStationStatus() == "CLOSED":
			print("Waiting to send ..." + str(datetime.now()))
			time.sleep(2)
		time.sleep(bigSleep)

def TopStories(messageInfo):
	
	s = JS8Client()
	
	try:	
		#2S5: N3WZ  [TOP]: NYT, 1
		mp = messageInfo.split(':')
		
		if len(mp) < 3:
			return

		callsign = mp[0].strip()
		step1 = mp[2].strip()
		step2 = step1.split(',')
		rn = step2[1].strip().split()
		src = step2[0].strip()	
		numberOf = rn[0].strip()

		print("Handling %s request ..." % src)
		print(messageInfo)
		print("From: " + callsign)
		print("Top: " + numberOf)

		tops = GetTop(src, numberOf)
			
		for t in tops:
			pubarr = str(t[2]).split()	
			pubdte = pubarr[0]
			# ID, TITLE, PUBLISHED
			ct = t[1].replace("''","'")
			
			# ID, TITLE, PUBLISHED, SOURCE, LOF
			
			lof = int(t[4])

			moreLink = ""
			
			if lof > 40:
				moreLink = "4MORE: N3WZ [ART]: %s" % t[0]	
			
			headline = "%s HEADLINE [%s] '%s', #%s %s" % (callsign, pubdte, ct, t[3], moreLink)  
			
			print(headline)
			while s.GetStationStatus() == "CLOSED":
				print("Waiting to send ..." + str(datetime.now()))
				time.sleep(5)
			s.SendToJS8(headline)
			while s.GetStationStatus() == "CLOSED":
				print("Waiting to send ..." + str(datetime.now()))
				time.sleep(5)
			time.sleep(bigSleep)		

	except:	
		print("An exception occurred: getting top stories")
	
def handleArticle(messageInfo):
	
	try:
		#K7IAC: N3W5 [ART]: 237 ♢  
		
		s = JS8Client()
		
		mp = messageInfo.split(':')
		
		if len(mp) < 3:
			return
		
		callsign = mp[0].strip()
		
		step1 = mp[2].strip().split()

		number = step1[0].strip()

		print("Handling article request ...")
		print(messageInfo)
		print("From: " + callsign)
		print("Article: " + number)

		recd = GetArt(number)
		
		art = recd[0]
		src = recd[1]
		lnk = recd[2]
		
		if len(art) > 150:
		
			art2 = art.replace("\n", " ")
			art2 = art2.replace("''","'")
		
			print("bigger than 150 chars")
			
			if len(art2) > 500:
				art2 = art2[0:500]
			
			pts = math.ceil((len(art2)/150))
			buffr = ""
			part = 1
			for c in art2:
				buffr += c
				if len(buffr) == 150:
				
					artInfo = "%s ART_%s %s #%s (%s/%s)" % (callsign, number, buffr, src, str(part), str(pts))  
				
					print(artInfo)
					
					s.SendToJS8(artInfo)
					
					buffr = ""
					part += 1
					time.sleep(1)
					while s.GetStationStatus() == "CLOSED":
						print("NEWS SERVICE TIME: " + str(datetime.now()))
						time.sleep(1)
					time.sleep(60)
			
			if len(buffr) > 0:
				artInfo = "%s ART_%s %s #%s (%s/%s)" % (callsign, number, buffr, src, str(part), str(pts))  
				print(artInfo)
				s.SendToJS8(artInfo)
				buffr = ""
			time.sleep(bigSleep)

		else:		
			art2 = art.replace("\n", " ")
			art2 = art2.replace("''","'")
			# ARTICLE, SOURCE
			artInfo = "%s ART_%s %s ... (MORE @ %s)" % (callsign, number, art2, src)  
			print(artInfo)
			while s.GetStationStatus() == "CLOSED":
				print("Waiting to send ..." + str(datetime.now()))
				time.sleep(5)
			s.SendToJS8(artInfo)
			while s.GetStationStatus() == "CLOSED":
				print("Waiting to send ..." + str(datetime.now()))
				time.sleep(5)
			time.sleep(bigSleep)
	except:
		print("An exception has occurred: getting news article")

def NewsCycle(DirectedPath):
	
	prevDLineNo = 0

	newsl = "news_line.txt"

	if(exists(newsl)):
		tf = open(newsl, "r")
		tfs = tf.read().strip()
		if(tfs != ""):
			prevDLineNo = int(tfs)
		tf.close()

	readerf = open(DirectedPath)

	CALLSIGN = ""
		
	UTCDTM = ""
		
	MSGINFO = ""

	dirLine = 0
		
	uploaded = 0
		
	try:
		# Further file processing goes here
		for x in readerf:

			#0 UTC,1 FREQ,2 OFFSET,3 SNR, 4 MESSAGE

			recd = x.split('\t')

			UTCDTM = recd[0]
			
			OFFSET = recd[2]
			
			SNR = recd[3]
			
			FREQ = recd[1]
			
			MSG = recd[4]
										
			dirLine += 1
				
			if(dirLine > prevDLineNo):
			
				if "[ART]:" in MSG:
					handleArticle(MSG)

				if "[TOP]:" in MSG:
					TopStories(MSG)

				wf = open(newsl, "w")
				wf.write(str(dirLine))
				wf.close()
	
	finally:
		readerf.close()

def CycleFeeds():
	infowars = "https://www.infowars.com/rss.xml"
	zh = "https://feeds.feedburner.com/zerohedge/feed"
	yahoo = "https://news.yahoo.com/rss/"
	cnn = "http://rss.cnn.com/rss/cnn_topstories.rss"
	bbc = "http://feeds.bbci.co.uk/news/world/us_and_canada/rss.xml"
	nyt = "https://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml"
	onion = "https://www.theonion.com/rss"
	bb = "https://babylonbee.com/feed"
	print("Grabbing Babylon Bee ...")
	GrabRSS(bb, "BB")
	print("Grabbing ONION ...")
	GrabRSS(onion, "ONION")
	print("Grabbing INFOWARS ...")
	GrabRSS(infowars, "INFOWARS")
	print("Grabbing ZEROHEDGE ...")
	GrabRSS(zh, "ZEROHEDGE")
	print("Grabbing YAHOO ...")
	GrabRSS(yahoo, "YAHOO")
	print("Grabbing CNN ...")
	GrabRSS(cnn, "CNN")
	print("Grabbing BBC ...")
	GrabRSS(bbc, "BBC")
	print("Grabbing NYT ...")
	GrabRSS(nyt, "NYT")

# FEEDS:
# 1. INFOWARS: https://www.infowars.com/rss.xml
# 2. ZEROHEDGE: https://feeds.feedburner.com/zerohedge/feed
# 3. YAHOO: https://news.yahoo.com/rss/
# 4. CNN: http://rss.cnn.com/rss/cnn_topstories.rss

clearConsole()
time.sleep(2)
print("Starting NEWS Server .")
time.sleep(1)
clearConsole()
print("Starting NEWS Server ..")
time.sleep(1)
clearConsole()
print("Starting NEWS Server ...")
time.sleep(1)
clearConsole()
print("Starting NEWS Server ....")
time.sleep(1)
clearConsole()

CycleFeeds()

if newsMode == 1:
	TopStoriesFromAll()

if newsMode == 2:

	ptime = datetime.now()
	
	while True:
		ntime = datetime.now()

		print("NEWS SERVICE: " + str(ntime))

		time.sleep(1)
		clearConsole()
		NewsCycle(DPath)	
		clearConsole()

		tdiff = ntime - ptime

		if tdiff.seconds > (60 * 5):
			print("Grabbing RSS feed info ...")
			ptime = datetime.now()
			CycleFeeds()
	
if newsMode == 3:
	TopStoriesFrom(newsServiceM3, 4)
	

RAD-TERMINAL: HOW TO HAVE FREE OFF GRID INTERNET 4-LIFE!

Where: https://www.preppershowsusa.com/prepper-show/be-prepared-expo-2022/

When: 4/23/2022, 11 AM MST (Utah Time)

Room: Classroom B

What: Class/Presentation “RAD ENGINEERING: FIFL”

Presenters: Justin Land and Dan Sullivan

Outline:

  1. Introduction: How we got into this, the story of RAD ENGINEERING. WHAT IS THE RAD TERMINAL? (5 minutes)
  2. Hardware: a) radio, b) computer, c) linkages and wiring … history of the radio tech and how it’s being used. JS8 (20 minutes)
  3. Software: history of WWW, what happened to TECH in the 70’s … why couldn’t we hook up TRS-80 / C64 computers to a CB in 1983 … what are bulletin boards? – how is radio LIKE a bulletin board … encryption strategies … encoding … ARS: Amateur Radio Services … what is an ARS? – remote procedure calls over CB radio, hitting python-agent receivers, hooking into actions … >sendto: >email: >payment: … command tags … EDI for radio? – small compact messaging AND payment services using voice recognition (20 minutes)
  4. Q/A (15 minutes)

THE RAD MANIFESTO: radengineering.tech

MP3: https://planetarystatusreport.com/mp3/20220225_A_RAD_MANIFESTO.mp3

Donate: https://www.paypal.com/paypalme/doctorfreckles

"If it doesn't work, we don't give up, we try something else." - Dr. Freckles (or someone he ripped off that said the same thing)

I’ll get this out of the way – the ideas presented below are not SPECIAL or even original … I dunno … I don’t think I’m a dummy, but I’m certain many dummies don’t think they are … I don’t think I’m special, I’m just tired of the LEARNED HELPLESSNESS. I think we have a right to dream and do, as we have a right to breathe.

We support RIGHT-TO-REPAIR at RADENGINEERING.TECH, everything we design, we document, software or hardware, will be OPEN … we can’t control our suppliers … but we can foster NEW freedom friendly suppliers …

What you see below is not our companies vision in any complete or limited form … we reserve the right, as we succeed or fail, to add new dreams to our list.

Is it possible others are working on now or have solved the problems listed below? – I HOPE THEY HAVE … and if they have, they should consider the open source and right-to-repair philosophy … it isn’t communism, it is human understanding. You can’t enforce this philosophy, but that’s ok … there will always be assholes. We want to make money, but we also want to change the world.

… finally …

We reserve the right to fail and try again.

We reserve the right to dream.

Here is what we dream of doing:

  1. THE RAD TERMINAL: this will begin as a home-brew movement, but we have the goal of building a ruggedized all-in-one terminal, with antenna deployment pack and DC battery power, within 1 year – version ONE of the RAD TERMINAL … RT-1. This system will be designed with right-to-repair features, to include simple, swappable, modules for components that have high variability within radio or computer design. As of this writing, 2/25/22, we are still in home brew phase.
  2. We talked about RTR (right-to-repair) already in terms of all our component designs and documentation.
  3. Rad TRUCK: this will be a self-sufficient (as possible given power features) communications node that would be useful in connection with WiFi based MESH networks AND as a local internet hub for folks that want their own local game nets, email, etc … and they don’t need to be connected ANYWHERE else.
  4. Project RTR #1: Local source all non-cpu, non-ram electronic components.
  5. Project RTR #2: Design and build a 3D printing system for CPU and RAM.
  6. We want to do #1 and #2 simultaneously, and that leads to project FREEDOM.
  7. Project Freedom: a project to build a 100% local sourced (within a few hundred miles, up to 2,000 miles) computer that is capable of running LINUX. It would be 100% right-to-repair friendly, to include a PDF describing how YOU TOO can build a system for 3D printing CPUs … and as much useful knowledge as we can share.
  8. Project Freedom has a sister project: Project RADIO: to design and build a 100% local sourced right-to-repair friendly radio system. We want to optimize component layout to make some parts of the radio optimized, stationary on the board (still repairable), and other parts in RTR-friendly component boxes that fold out, from a cube or cube-like module, where anyone can work on those parts too … but, perhaps certain intrinsically different radio components can be placed in a swap module component box – and that would mean switching bands/frequencies/wavelengths, event to include WiFi, would be as simple as swapping out a plastic plug, with an easy pop button.
  9. Project Tesla: a remotely controlled drone for radio-repeater operations. This will allow the use of microwave technology for data exchange, at high altitudes, and this system LINKED to ground RAD TERMINAL devices for even higher rates of no-nonsense communication.
  10. Project Space Diver: this will be our first foray into aerospace and tourism/entertainment/travel. We will run a “you can jump from the edge of space” business, where we design a very safe “pod” that is lifted to 20 miles, and has a velocity reduction/control multi-stage parachute system. Plus, we want to design a reusable/rechargeable balloon system, for hydrogen at first … the dangers from combustion are vastly reduced as you go up in altitude. This will be a way for people to see the “curve”, and I think we can reach a price point of around $1000.00 per dive. Again – they will be in an aerodynamically sound pod, with a pressure suit on in case of decompression. We will have as many safeguards against accidental harm as is feasible – but we’re talking about 20 miles up … so it’s dangerous, no matter the pod.
  11. Project S’klider: this will be our second foray into aerospace. This will be a tourism/entertainment like “Space Diver”, but we’re hoping ordinary people will be able to buy them, here’s the concept: design a lightweight glider, for high altitudes, that can fold into a gondola for a blimp type hydrogen balloon system. At say, 15 miles up, the balloon deflates and is pulled back into the glider, and the glider can then proceed, folding out its wings, like a well designed glider for high/medium altitudes. The S’klider should be able to repeat this maneuver, even in mid air, but slowly filling the balloon back up with stored hydrogen. The idea: you could glide a LONG WAYS, especially if you have atmospheric thermal mapping hardware and software, with a well designed glider … perhaps thousands of miles with AI computer assistance for optimal thermal altitude gain.
  12. Project FAST: quantum radio … I think IBM has been sitting on this … it would be hyper-bandwidth powerful, hyper-de-centralized, hyper-liberating … the internet would be dead after this … pretty much everyone would use a quantum radio … and encryption? – that gets fun using synchronized noise for message embedding … or quantum steganography …. fun …
  13. Project View: a high altitude array of dirigible micro-observatories … astronomical in nature. We would like to take high resolution pictures by combining many smaller high altitude drones into one large array optical telescope. You could have these all around the globe. They could assist in the early detection of dangerous near earth objects.
  14. Project Vacuum: the design and construction of a vacuum ship using magnetic field engineering to create an inner structure of force, whereby creating mass-free structure that can encapsulate a vacuum. This kind of ship would be “lighter than air” but in a very powerful way and it could allow for single-stage-to-orbit rocket ships.
  15. Project Orbit: if Project Vacuum is successful, build a convertible vacuum ship dirigible system, with a gondola system designed like a lifting body glider, so that in case of vacuum envelope failure, the gondola itself could breakaway and the passengers could land safely. This ship would use a standard chemical rocket engine to achieve orbit, but would be lifted by its vacuum envelopes to an optimal altitude. The ship would be capable of maneuvering around the planet, at 20 miles, to find an optimal launch location for orbit.
  16. Project D.B. Cooper: a jacket/pant system that, if worn onto a plane, would allow a person to have a better chance of surviving a catastrophic failure of a passenger airliner. The system would be designed with carbon fiber and kevlar structuring to ensure protection against shrapnel, and an easy to deploy parachute system. For more money? – you get the 20 lb raft/radio combo … not sure about the sharks though.
  17. Project Mars: Orbit + synthetic gravity drive
  18. Project Stellar: Mars + focal synthetic gravity => space time cavitation => low density space time => mole drive, basically a mole drive using synthetic, projected, moments of synthetic gravity. This would reduce space time distance, the same way a mole, tunneling through the earth does it … little bits at a time … but the attraction principles would become nonlinear as speed increased, and this is an interesting area of research. Bottom line: worm holes work, but are very dangerous … this usese Einstein-Rosen Bridge concepts, but on a micro basis
  19. G-Ray: Build a gamma ray laser …
  20. We want to sponsor an expedition to Antarctica … Justin might not want to go, that’s ok, I might want him to man our base station repeater system as I read AT THE MOUNTAINS OF MADNESS … from fucking Antarctica … over shortwave radio with repeaters. But we also want to map the continent, and explore it.
  21. Freeze Ray: discover a useful and non-hazardous quantum endothermic particle that would cancel out the effects of photons
  22. New ZTuff: design a means of constructing useful synthetic matter from pure energy …
  23. OPEN PHP: if there isn’t an open php initiative, I want to start one … easy custom builds … transparent module viewing AND swapping for custom builds … and an ALL IN ONE deployable PHP friendly web server … other languages/APIs will work with it, but the most advanced, easy to control, PHP language and server will always be the best. We will also develop a simplified serialization and storage system that installs with the system, you can set how much memory you want it to use, both RAM and hard drive …
  24. Temperature tolerant cheap and durable super conductive material, with the same malleability features of copper.
  25. We want to foster the mentoring of young people in technology, sciences, math, so that our lives, though never perfect, can be far more kind and at peace and joy filled.

(we won’t be doing time travel – that’s crap)

JS8 Commands for API (TCP/IP)

Messages from JS8Call
CLOSE – JS8Call has been closed
INBOX.MESSAGES – A list of inbox messages
INBOX.MESSAGE – A single inbox message
MODE.SPEED – The current TX speed
PING – Keepalive from JS8Call
RIG.FREQ – The rig frequency has been changed
RIG.PTT – PTT has been toggled
RX.ACTIVITY – We received something
RX.BAND_ACTIVITY – The band activity window
RX.CALL_ACTIVITY – The call activity window
RX.CALL_SELECTED – The currently selected callsign
RX.DIRECTED – A complete message
RX.SPOT – A station we have heard
RX.TEXT – Contents of the QSO window
STATION.CALLSIGN – Callsign of the station
STATION.GRID – Grid locator of the station
STATION.INFO – QTH/info of the station
STATION.STATUS – Status of the station
TX.FRAME – Something we are sending
TX.TEXT – Text in the outgoing message window

Messages to JS8Call
INBOX.GET_MESSAGES – Get a list of inbox messages
INBOX.STORE_MESSAGE – Store a message in the inbox
MODE.GET_SPEED – Get the TX speed
MODE.SET_SPEED – Set the TX speed
RIG.GET_FREQ – Get the current dial freq and offset
RIG.SET_FREQ – Set the current dial freq and offset
RX.GET_BAND_ACTIVITY – Get the contents of the band activity window
RX.GET_CALL_ACTIVITY – Get the contents of the call activity window
RX.GET_CALL_SELECTED – Get the currently selected callsign
RX.GET_TEXT – Get the contents of the QSO qindow
STATION.GET_CALLSIGN – Get the station callsign
STATION.GET_GRID – Get the station grid
STATION.SET_GRID – Set the station grid
STATION.GET_INFO – Get the station QTH/info
STATION.SET_INFO – Set the station QTH/info
STATION.GET_STATUS – Get the station status
STATION.SET_STATUS – Set the station status
TX.SEND_MESSAGE – Send a message via JS8Call
TX.SET_TEXT – Sets the text in the outgoing message box, but does not send it.
WINDOW.RAISE – Focus the JS8Call window

The python example below is modified form the JS8 source code available here: http://js8call.com/

from __future__ import print_function

from socket import socket, AF_INET, SOCK_STREAM

import json
import time

#make sure you open port 2442 prior to opening JS8 application

#ubuntu command: sudo ufw allow 2442

server = ('127.0.0.1', 2442)

def from_message(content):
    try:
        return json.loads(content)
    except ValueError:
        return {}


def to_message(typ, value='', params=None):
    if params is None:
        params = {}
    return json.dumps({'type': typ, 'value': value, 'params': params})


class Client(object):
    first = True
    def process(self, message):
        typ = message.get('type', '')
        value = message.get('value', '')
        params = message.get('params', {})
        if not typ:
            return

        if typ in ('RX.ACTIVITY',):
            # skip
            return

        print('->', typ)

        if value:
            print('-> value', value)

        if params:
            print('-> params: ', params)


    def send(self, *args, **kwargs):
        params = kwargs.get('params', {})
        if '_ID' not in params:
            params['_ID'] = '{}'.format(int(time.time()*1000))
            kwargs['params'] = params
        message = to_message(*args, **kwargs)
        print('outgoing message:', message)
        self.sock.send((message + '\n').encode()) # remember to send the newline at the end :)
    
    def send2(self):
        message = "{'type': 'STATION.GET_STATUS', 'value': '', 'params': {'_ID': '1645105873058'}}"
        print('outgoing message:', message)
        self.sock.send((message + '\n').encode()) # remember to send the newline at the end :)
    
    def connect(self):
        print('connecting to', ':'.join(map(str, server)))
        self.sock = socket(AF_INET, SOCK_STREAM)
        self.sock.connect(server)
        self.connected = True
        try:
            # send a simple example query after connected

            self.send("TX.SET_TEXT", "WHAT IS GOING ON?")
			
            #list of commands here: 
            #https://planetarystatusreport.com/?p=646
            
            while self.connected:
                content = self.sock.recv(65500)
                if not content:
                    break
                print('incoming message')

                try:
                    message = json.loads(content)
                except ValueError:
                    message = {}

                if not message:
                    continue

                self.process(message)

        finally:
            self.sock.close()

    def close(self):
        self.connected = False

def main():
    s = Client()
    s.connect()

if __name__ == '__main__':
    main()