#!/usr/bin/env python3 # Infos on colors: https://pypi.org/project/colored/ # Country list extracted from http://www.arrl.org/files/file/dxcclist.txt import sys import csv import re import json import os import time as bla from telnetlib import Telnet from colored import fg, bg, attr import configparser from collections import defaultdict import random import requests from os.path import exists import zipfile class ColorSpot(): def __init__(self): self.version = "0.1.0" self.print_banner() self.config = configparser.ConfigParser() self.config_file = os.path.expanduser('~/.colorspot.ini') self.read_config(self.config, self.config_file) self.check_files() if self.check_lotw_confirmed: self.confirmed_entities = self.get_confirmed_entities() if self.check_cty: self.cty = list(csv.reader(open(self.config['files']['cty'], "r"), delimiter=",")) @staticmethod def rnd_col(): r = lambda: random.randint(0,255) return'#%02X%02X%02X' % (r(),r(),r()) @staticmethod def download_file(url, local_filename): with requests.get(url, stream=True) as r: r.raise_for_status() with open(local_filename, 'wb') as f: for chunk in r.iter_content(chunk_size=8192): f.write(chunk) return local_filename def check_files(self): # check for lotw qsl information file self.check_lotw_confirmed = exists(self.config['files']['lotw_confirmed']) if not self.check_lotw_confirmed: print("The file " + self.config['files']['lotw_confirmed'] + " is missing.") user = self.config['lotw']['user'] password = self.config['lotw']['password'] mode = self.config['lotw']['mode'] url = "https://lotw.arrl.org/lotwuser/lotwreport.adi?login={}&password={}&qso_query=1&qso_qsl=yes&qso_mode={}&qso_qsldetail=yes&qso_qslsince=1970-01-01".format(user, password, mode) print("Trying to download " + url) file_name = self.download_file(url, self.config['files']['lotw_confirmed']) self.check_lotw_confirmed = exists(self.config['files']['lotw_confirmed']) if self.check_lotw_confirmed: print("File successfully downloaded") else: print("something went wrong while downloading " + url) # check for cty.csv file self.check_cty = exists(self.config['files']['cty']) if not self.check_cty: url = self.config['files']['cty_url'] print("The file " + self.config['files']['cty'] + " is missing.") print("Trying to download " + url) zip_name = self.download_file(url, "bigcty.zip" ) with zipfile.ZipFile(zip_name, 'r') as zip_ref: zip_ref.extract("cty.csv") os.remove(zip_name) self.check_cty = exists(self.config['files']['cty']) if self.check_cty: print("File successfully downloaded and extracted.") else: print("something went wrong while downloading " + url) # check for lotw user activity file self.check_lotw_activity = exists(self.config['files']['lotw_activity']) if not self.check_lotw_activity: url = self.config['files']['lotw_activity_url'] print("The file " + self.config['files']['lotw_activity'] + " is missing.") print("Trying to download " + url) file_name = self.download_file(url, self.config['files']['lotw_activity']) self.check_lotw_activity = exists(self.config['files']['lotw_activity']) if self.check_lotw_activity: print("File successfully downloaded") else: print("something went wrong while downloading " + url) def print_banner(self): """print an awesome banner""" ver = self.version # print the banner print(fg(self.rnd_col())+" ___ _ ___ _ ") print(fg(self.rnd_col())+" / __|___| |___ _ _/ __|_ __ ___| |_ ") print(fg(self.rnd_col())+" | (__/ _ \ / _ \ '_\__ \ '_ \/ _ \ _|") print(fg(self.rnd_col())+" \___\___/_\___/_| |___/ .__/\___/\__|") print(fg(self.rnd_col())+" -= DK1MI =- |_| ") print("") print(attr('reset')) @staticmethod def read_config(config, file_name): """reads the configuration from the config file or creates a default config file if none could be found""" if os.path.isfile(file_name): config.read(file_name) else: config = configparser.ConfigParser() config['cluster'] = { 'host': 'dxc.nc7j.com', 'port': '7373', 'user': 'N0CALL', 'timeout': '100'} config['files'] = { 'cty': 'cty.csv', 'cty_url': 'https://www.country-files.com/bigcty/download/bigcty.zip', 'lotw_confirmed': 'lotw.adi', 'lotw_activity': 'lotw-user-activity.csv', 'lotw_activity_url': 'https://lotw.arrl.org/lotw-user-activity.csv'} config['lotw'] = { 'user': 'N0CALL', 'password': 'XXXXXXXXX', 'mode': 'ssb'} config['band_colors'] = { "145": "white", "144": "white", "50": "white", "29": "yellow", "28": "yellow", "24": "red", "21": "orchid", "18": "green", "14": "steel_blue_3", "10": "orange_1", "7": "cyan", "5": "white", "3": "light_cyan", "1": "white", 'alert_bg': 'indian_red_1a', 'alert_fg': 'white', 'default_bg': 'black'} config['cont_colors'] = { "AF": "light_salmon_3b", "AN": "white", "AS": "orange_red_1", "EU": "cyan", "NA": "steel_blue_3", "OC": "orchid", "SA": "light_goldenrod_2a"} config['colors']= { 'use_colors': 'yes', 'color_by' : 'continent', 'alert_bg': 'indian_red_1a', 'alert_fg': 'white', 'default_bg': 'black'} with open(file_name, 'w') as configfile: config.write(configfile) print("\nNo configuration file found. A new configuration file has been created.") print("\nPlease edit the file " + file_name + " and restart the application.\n" ) sys.exit() return config def get_confirmed_entities(self): ret = [] #TODO: download file and/or tell user what to do file = open(self.config['files']['lotw_confirmed'], "r") for row in file: if re.search("")[2].lower().rstrip() if dxcc not in ret: ret.append(dxcc) return ret def check_lotw(self, call): ret = "" #TODO: download file and/or tell user what to do csv_file = csv.reader(open(self.config['files']['lotw_activity'], "r"), delimiter=",") #loop through the csv file for row in csv_file: if call == row[0]: ret = row[1] return ret return ret def get_cty_row(self, call): done = False while not done: for row in self.cty: entities = row[9].replace(";", "").replace("=", "").split(" ") # TODO: schauen ob = davor und match -> als special call anzeigen for prefix in entities: if call == prefix: return row call = call[:-1] if call == "": return ["-", "-", "-", "-", "-", "-", "-"] return None def get_spots(self): with Telnet(self.config['cluster']['host'], int(self.config['cluster']['port']), \ int(self.config['cluster']['timeout'])) as telnet: while True: line_enc = telnet.read_until(b"\n") # Read one line line = line_enc.decode('ascii') if "enter your call" in line: b_user = str.encode(self.config['cluster']['user']+"\n") telnet.write(b_user) elif " Hello " in line: print(fg("grey_27") + line + attr("reset")) foreground = "white" background = "grey_27" sep = fg("grey_27")+'|'+fg(foreground) row = ["DE", sep, "Freq", sep, "DX", \ sep, "Country", sep, "C", sep, "L", sep, "Comment", sep, "Time"] print(bg(background) + fg(foreground) + \ '{:9.9} {:<1} {:>7.7} {:<1} {:<10.10} {:<1} {:<16.16} {:<1} {:<2.2} {:<1} {:<1.1} {:<1} {:<30.30} {:<1} {:<4.4}'.format(*row) + attr("reset")) b_cmd = str.encode("sh/dx/50 @\n") telnet.write(b_cmd) elif "DX de" in line or "Dx de" in line: try: band_col = "" call_de = re.search('D(X|x) de (.+?): ', line).group(2) freq = re.search(': +(.+?) ', line).group(1) call_dx = re.search(freq + ' +(.+?) ', line).group(1) time = re.search('[^ ]*$', line).group(0)[0:4] comment = re.search(call_dx + ' +(.+?) +' + time, line).group(1) if self.check_cty: cty_details = self.get_cty_row(call_dx) else: cty_details = ["-","-","-","-","-","-","-","-","-","-"] areaname = cty_details[1] continent = cty_details[3] if self.check_lotw_activity and self.check_lotw(call_dx): lotw = "✓" else: lotw = "" try: if self.config['colors']['color_by'] == "band": foreground = self.config['band_colors'][freq[:-5]] elif self.config['colors']['color_by'] == "continent": foreground = self.config['cont_colors'][continent] else: foreground = "white" except Exception: foreground = "white" freq = freq.replace('.0', '') if self.check_lotw_confirmed and cty_details[2] not in self.confirmed_entities: background = self.config['colors']['alert_bg'] foreground = self.config['colors']['alert_fg'] else: background = self.config['colors']['default_bg'] sep = fg("grey_27")+'|'+fg(foreground) row = [call_de, sep, freq, sep, call_dx, \ sep, areaname, sep, continent, sep, lotw, sep, comment, sep, time] print(bg(background) + fg(foreground) + \ '{:9.9} {:<1} {:>7.7} {:<1} {:<10.10} {:<1} {:<16.16} {:<1} {:<2.2} {:<1} {:<1.1} {:<1} {:<30.30} {:<1} {:<4.4}'.format(*row) + attr("reset")) except AttributeError: print(line) ##################################################### # Main Routine # ##################################################### def main(): try: color_spot = ColorSpot() color_spot.get_spots() except KeyboardInterrupt: sys.exit(0) # or 1, or whatever if __name__ == "__main__": try: sys.exit(main()) except EOFError: pass