Skip to content

Instantly share code, notes, and snippets.

@wjlafrance
Last active August 1, 2025 17:42
Show Gist options
  • Save wjlafrance/078d14d885072d246a4d66dd24ff82c7 to your computer and use it in GitHub Desktop.
Save wjlafrance/078d14d885072d246a4d66dd24ff82c7 to your computer and use it in GitHub Desktop.
Vibe-coded net controller script
// authored entirely by ChatGPT with my prompting
// radioCheckInCLI.ts
import * as readline from "readline";
import * as fs from "fs";
import * as path from "path";
interface CheckIn {
name?: string;
callsign: string;
location?: string;
traffic: "NTS" | "Local" | "Informal" | "None";
summary?: string;
timestamp: string;
}
interface Regular {
name: string;
callsign: string;
location: string;
}
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
let checkIns: CheckIn[] = [];
let regulars: Regular[] = [];
function loadRegulars() {
try {
const data = fs.readFileSync(path.join(process.cwd(), "regulars.json"), "utf-8");
regulars = JSON.parse(data);
} catch {
console.warn("⚠️ Could not load regulars.json — continuing without it.");
}
}
function saveCheckInsToFile() {
const date = new Date().toISOString().split("T")[0];
fs.writeFileSync(`${date}_checkins.json`, JSON.stringify(checkIns, null, 2));
}
function ask(question: string): Promise<string> {
return new Promise((resolve) => rl.question(question, resolve));
}
async function startCheckInLoop() {
let keepGoing = true;
while (keepGoing) {
const keepGoingAfter = await startCheckIn();
keepGoing = keepGoingAfter;
}
}
async function startCheckIn(): Promise<boolean> {
const entry: Partial<CheckIn> = { traffic: "None" };
while (!entry.callsign || entry.callsign.trim() === "") {
const callsignInput = (await ask("Enter callsign (required): ")).trim();
if (callsignInput === "") {
return false; // return to main menu if empty
}
entry.callsign = callsignInput.toUpperCase();
}
const regular = regulars.find(
(r) => r.callsign.toLowerCase() === entry.callsign!.toLowerCase()
);
if (regular) {
console.log(`Matched regular: ${regular.name} from ${regular.location}`);
entry.name = regular.name;
entry.location = regular.location;
}
let done = false;
while (!done) {
console.log(`\n📝 Check-in for ${entry.callsign}
[c] Callsign: ${entry.callsign}
[n] Name: ${entry.name || "(not set)"}
[l] Location: ${entry.location || "(not set)"}
[t] Traffic: ${entry.traffic}
[s] Save
[a] Save and another
[x] Cancel
`);
const choice = (await ask("Select field: ")).toLowerCase();
switch (choice) {
case "c":
entry.callsign = (await ask("Enter callsign: ")).trim().toUpperCase();
break;
case "n":
entry.name = (await ask("Enter name: ")).trim();
break;
case "l":
entry.location = (await ask("Enter location: ")).trim();
break;
case "t":
console.log("[1] NTS\n[2] Local\n[3] Informal\n[4] None");
const trafficChoice = await ask("Select traffic type: ");
entry.traffic =
trafficChoice === "1"
? "NTS"
: trafficChoice === "2"
? "Local"
: trafficChoice === "3"
? "Informal"
: "None";
break;
case "s":
case "a":
if (
entry.callsign ||
(entry.name && entry.name.trim()) ||
(entry.location && entry.location.trim())
) {
entry.timestamp = new Date().toISOString();
checkIns.push(entry as CheckIn);
console.log("✅ Check-in recorded.");
saveCheckInsToFile();
await new Promise((res) => setTimeout(res, 500));
return choice === "a"; // true if user chose 'a' (Save and another)
} else {
console.log("⚠️ Please complete at least one of Name, Callsign, or Location before saving.");
}
break;
case "x":
console.log("❌ Check-in canceled.");
done = true;
break;
default:
console.log("Unknown option.");
}
}
return false;
}
async function takeTraffic() {
const sorted = [...checkIns].sort((a, b) => {
const priority = { NTS: 1, Local: 2, Informal: 3, None: 4 };
return priority[a.traffic] - priority[b.traffic];
});
let done = false;
while (!done) {
console.log("\n📻 Traffic Check — Select a station:");
sorted.forEach((c, i) => {
console.log(`[${i + 1}] ${c.callsign} (${c.name || "Unknown"}) — ${c.traffic}`);
});
console.log("[x] Back to main menu\n");
const input = (await ask("Select station: ")).toLowerCase();
if (input === "x") {
done = true;
} else {
const index = parseInt(input) - 1;
if (index >= 0 && index < sorted.length) {
const station = sorted[index];
station.summary = await ask("Enter summary of traffic: ");
console.log("✅ Summary recorded.");
saveCheckInsToFile();
} else {
console.log("Invalid selection.");
}
}
}
}
async function main() {
loadRegulars();
let done = false;
while (!done) {
console.log("\n📋 Net Control Menu\n[n] New check-in\n[l] List check-ins\n[t] Take traffic\n[q] Quit\n");
const input = (await ask("Select an option: ")).toLowerCase();
switch (input) {
case "n":
await startCheckInLoop();
break;
case "l":
console.log("\nCurrent check-ins:");
checkIns.forEach((c, i) => {
console.log(
`${i + 1}. ${c.callsign} - ${c.name || "Unknown"} - ${c.location || "Unknown"} - ${c.traffic}`
);
});
break;
case "t":
await takeTraffic();
break;
case "q":
done = true;
break;
default:
console.log("Unknown option.");
}
}
rl.close();
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment