Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d917e14
flight tracker regex check
julestheshiba Nov 12, 2025
b5af9ea
flight tracker started
julestheshiba Nov 12, 2025
cde4df3
flight tracker refactored and properly setup
julestheshiba Nov 13, 2025
8b2fd11
more flight tracker
julestheshiba Nov 23, 2025
d3f1dba
basic flight tracking
julestheshiba Nov 24, 2025
352f9ba
planeinfo command working
julestheshiba Nov 24, 2025
1b0e7f8
started flight progress
julestheshiba Nov 24, 2025
a8f6046
current version of flight tracker
julestheshiba Nov 26, 2025
16ca9e5
Add schema and fix config file finding.
coravacav Nov 27, 2025
f89eb64
Updated and fixed errors
julestheshiba Nov 27, 2025
c897916
flight tracker regex check
julestheshiba Nov 12, 2025
1bb08b9
flight tracker started
julestheshiba Nov 12, 2025
f6ef285
flight tracker refactored and properly setup
julestheshiba Nov 13, 2025
72fa72c
more flight tracker
julestheshiba Nov 23, 2025
e3030ba
basic flight tracking
julestheshiba Nov 24, 2025
16e9cf9
planeinfo command working
julestheshiba Nov 24, 2025
e1fa542
started flight progress
julestheshiba Nov 24, 2025
ae3b1c2
current version of flight tracker
julestheshiba Nov 26, 2025
2614a4b
Updated and fixed errors
julestheshiba Nov 27, 2025
b09a95e
Merge branch 'main' of https://github.com/julestheshiba/uofu-cs-disco…
julestheshiba Nov 27, 2025
dd8a452
switched to embed discord prints
julestheshiba Nov 27, 2025
c2167f2
bot working v1
julestheshiba Nov 27, 2025
40987e1
removed guild id
julestheshiba Nov 27, 2025
a79676f
removed local config
julestheshiba Nov 27, 2025
356553e
actually removed
julestheshiba Nov 27, 2025
660407e
added newline in Cargo.toml
julestheshiba Nov 27, 2025
bbb6032
moved airport lookup to if statement to reduce api calls
julestheshiba Nov 28, 2025
c54177f
removed log file
julestheshiba Nov 29, 2025
9651596
deleted
julestheshiba Nov 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
256 changes: 165 additions & 91 deletions bot-lib/src/commands/track_flight.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
//use std::f64::consts::PI;
use std::f64::consts::PI;
use crate::data::PoiseContext;
use color_eyre::{eyre::{Result, eyre}};
use poise::{CreateReply, serenity_prelude::{CreateEmbed, CreateEmbedFooter, colours::{branding::BLURPLE, roles::BLUE}}};
use serde::Deserialize;
use chrono::{Local, Datelike};

// #[derive(Debug, Deserialize)]
// struct AirportResponse {
// response: Option<AirportData>,
// error: Option<AirlabsError>,
// }
#[derive(Debug, Deserialize)]
struct AirportResponse {
response: Option<Vec<AirportData>>,
error: Option<AirlabsError>,
}

// #[derive(Debug, Deserialize)]
// struct AirportData {
// lat: Option<f64>,ß
// lng: Option<f64>,
// }
#[derive(Debug, Deserialize)]
struct AirportData {
lat: Option<f64>,
lng: Option<f64>,
}

#[derive(Debug, Deserialize)]
struct FlightResponse {
Expand All @@ -36,17 +36,18 @@ struct FlightData {
dep_time: Option<String>,
arr_time: Option<String>,
engine: Option<String>,
// age: Option<i64>,
built: Option<i64>,
speed : Option<i64>,
alt : Option<i64>,
arr_estimated: Option<String>,
dep_estimated: Option<String>,
flight_icao: Option<String>,
airline_icao: Option<String>,
dep_icao: Option<String>,
arr_icao: Option<String>,
airline_name: Option<String>,
// lat: Option<f64>,
// long: Option<f64>,
lat: Option<f64>,
lng: Option<f64>,
}

#[derive(Debug, Deserialize)]
Expand All @@ -55,11 +56,19 @@ struct AirlabsError {
}

static IATA_RE: std::sync::LazyLock<regex::Regex> = std::sync::LazyLock::new(|| {
regex::Regex::new(r"^[A-Z]{2}[0-9]{1,4}").unwrap()
regex::Regex::new(r"^[A-Z]{2}[0-9]{1,4}$").unwrap()
});

static ICAO_RE: std::sync::LazyLock<regex::Regex> = std::sync::LazyLock::new(|| {
regex::Regex::new(r"^[A-Z]{3}[0-9]{1,4}").unwrap()
regex::Regex::new(r"^[A-Z]{3}[0-9]{1,4}$").unwrap()
});

static IATA_RE_AIRP: std::sync::LazyLock<regex::Regex> = std::sync::LazyLock::new(|| {
regex::Regex::new(r"^[A-Z]{3}$").unwrap()
});

static ICAO_RE_AIRP: std::sync::LazyLock<regex::Regex> = std::sync::LazyLock::new(|| {
regex::Regex::new(r"^[A-Z]{4}$").unwrap()
});

fn format_time(label: &str, scheduled: &Option<String>, estimated: &Option<String>) -> String {
Expand All @@ -79,72 +88,80 @@ fn minutes_to_hours(duration: Option<i64>) -> String {
}
/* work in progress for flight progress tracking
*/
fn progress_bar(percentage: f64) -> String {
let total_blocks = 10;
let clamped_percentage = percentage.clamp(0.0, 1.0);
let filled_blocks = (clamped_percentage * total_blocks as f64).round() as usize;
let empty_blocks = total_blocks - filled_blocks;

let filled_part = "=".repeat(filled_blocks);
let empty_part = "-".repeat(empty_blocks);
let leading_char = if filled_blocks < total_blocks { "✈️" } else { "" };

format!("{}{}{}", filled_part, leading_char, empty_part)
}

fn degree_to_rad(deg :f64) -> f64{
deg * PI / 180.0
}

// fn degree_to_rad(deg :f64) -> f64{
// deg * PI / 180.0
// }

// fn haversine_distance(lat1: f64, long1: f64, lat2: f64, long2: f64) -> f64 {
// let r = 6371.0;
// let dlat = degree_to_rad(lat2 - lat1);
// let dlon = degree_to_rad(long2 - long1);
// let lat1 = degree_to_rad(lat1);
// let lat2 = degree_to_rad(lat2);

// let a = (dlat/2.0).sin().powi(2) +
// lat1.cos() * lat2.cos() * (dlon/2.0).sin().powi(2);
// let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());
// r * c
// }

// fn flight_progess(plane_lat: f64, plane_long: f64, source_lat: f64, source_long: f64, dst_lat: f64, dst_long: f64) -> f64{
// let d_star_dest = haversine_distance(source_lat, source_long, dst_lat, dst_long);
// let d_start_airp = haversine_distance(source_lat, source_long, plane_lat, plane_long);
// d_star_dest / d_start_airp
// }

// async fn airport_lookup(ctx: PoiseContext<'_>, code: String) -> Option<AirportData> {
// let api_key = std::env::var("API_KEY").map_err(|_| eyre!("API_KEY missing from environment")).ok()?;
// let searched_iata = IATA_RE.is_match(&code);
// let searched_icao = ICAO_RE.is_match(&code);

// let url = if searched_iata {
// format!(
// "https://airlabs.co/api/v9/airport?iata_code={}&api_key={}",
// code, api_key
// )
// } else if searched_icao {
// format!(
// "https://airlabs.co/api/v9/airport?icao_code={}&api_key={}",
// code, api_key
// )
// } else {
// ctx.reply("Please provide a valid airport ident(IATA or ICAO)").await.ok()?;
// return None;
// };

// let client = reqwest::Client::new();
// let response: AirportResponse = client
// .get(url)
// .send()
// .await
// .ok()?
// .json()
// .await
// .ok()?;

// if let Some(err) = response.error {
// ctx.reply(format!("API Error: {}", err.message)).await.ok()?;
// return None;
// }

// let Some(airport) = response.response else {
// ctx.reply("No airport data found for that code.").await.ok()?;
// return None;
// };

// Some(airport)
// }
fn haversine_distance(lat1: f64, long1: f64, lat2: f64, long2: f64) -> f64 {
let r = 6371.0;
let dlat = degree_to_rad(lat2 - lat1);
let dlon = degree_to_rad(long2 - long1);
let lat1 = degree_to_rad(lat1);
let lat2 = degree_to_rad(lat2);

let a = (dlat/2.0).sin().powi(2) +
lat1.cos() * lat2.cos() * (dlon/2.0).sin().powi(2);
let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());
r * c
}

fn flight_progess(plane_lat: f64, plane_long: f64, source_lat: f64, source_long: f64, dst_lat: f64, dst_long: f64) -> f64{
let total_distance = haversine_distance(source_lat, source_long, dst_lat, dst_long);
let traveled_distance = haversine_distance(source_lat, source_long, plane_lat, plane_long);
if total_distance == 0.0 {
return 0.0;
}
(traveled_distance / total_distance).clamp(0.0, 1.0)
}

async fn airport_lookup(code: String) -> Result<AirportData> {
let api_key = std::env::var("API_KEY").map_err(|_| eyre!("API_KEY missing from environment"))?;
let searched_iata = IATA_RE_AIRP.is_match(&code);
let searched_icao = ICAO_RE_AIRP.is_match(&code);

let url = if searched_iata {
format!(
"https://airlabs.co/api/v9/airports?iata_code={}&api_key={}",
code, api_key
)
} else if searched_icao {
format!(
"https://airlabs.co/api/v9/airports?icao_code={}&api_key={}",
code, api_key
)
} else {
return Err(eyre!("Invalid airport code: {}", code));
};

let client = reqwest::Client::new();
let response: AirportResponse = client
.get(url)
.send()
.await?
.json()
.await?;

if let Some(err) = response.error {
return Err(eyre!("API Error: {}", err.message));
}

let airports = response.response.ok_or_else(|| eyre!("No airport data found for code: {}", code))?;

airports.into_iter().next().ok_or_else(|| eyre!("Airport list was empty for code: {}", code))
}

async fn flight_lookup(ctx: PoiseContext<'_>, code: String) -> Option<FlightData> {
let api_key = std::env::var("API_KEY").map_err(|_| eyre!("API_KEY missing from environment")).ok()?;
Expand Down Expand Up @@ -215,43 +232,100 @@ pub async fn track_flight(
let (flight_label, dep_airport, arr_airport) = if searched_iata {
(
flight.flight_iata.clone().or(flight.flight_icao.clone()).unwrap_or_default(),
flight.dep_iata.clone().or(flight.dep_icao.clone()).unwrap_or_else(|| "N/A".to_string()),
flight.arr_iata.clone().or(flight.arr_icao.clone()).unwrap_or_else(|| "N/A".to_string()),
flight.dep_iata.clone().or(flight.dep_icao.clone()),
flight.arr_iata.clone().or(flight.arr_icao.clone()),
)
} else if searched_icao {
(
flight.flight_icao.clone().or(flight.flight_iata.clone()).unwrap_or_default(),
flight.dep_icao.clone().or(flight.dep_iata.clone()).unwrap_or_else(|| "N/A".to_string()),
flight.arr_icao.clone().or(flight.arr_iata.clone()).unwrap_or_else(|| "N/A".to_string()),
flight.dep_icao.clone().or(flight.dep_iata.clone()),
flight.arr_icao.clone().or(flight.arr_iata.clone()),
)
} else {
(
flight.flight_iata.clone().or(flight.flight_icao.clone()).unwrap_or_default(),
flight.dep_iata.clone().or(flight.dep_icao.clone()).unwrap_or_else(|| "N/A".to_string()),
flight.arr_iata.clone().or(flight.arr_icao.clone()).unwrap_or_else(|| "N/A".to_string()),
flight.dep_iata.clone().or(flight.dep_icao.clone()),
flight.arr_iata.clone().or(flight.arr_icao.clone()),
)
};

let Some(dep_code) = dep_airport else {
ctx.reply("Departure airport code not available for this flight.").await?;
return Ok(());
};
let Some(arr_code) = arr_airport else {
ctx.reply("Arrival airport code not available for this flight.").await?;
return Ok(());
};

let depart_airport = match airport_lookup(dep_code.clone()).await {
Ok(airport) => airport,
Err(e) => {
ctx.reply(format!("Failed to lookup departure airport {}: {}", dep_code.clone(), e)).await?;
return Ok(());
}
};

let arrival_airport = match airport_lookup(arr_code.clone()).await {
Ok(airport) => airport,
Err(e) => {
ctx.reply(format!("Failed to lookup arrival airport {}: {}", arr_code.clone(), e)).await?;
return Ok(());
}
};

let aicraft_lat = flight.lat.unwrap_or(0.0);
let aicraft_long = flight.lng.unwrap_or(0.0);
let source_airport_lat = depart_airport.lat.unwrap_or(0.0);
let source_airport_long = depart_airport.lng.unwrap_or(0.0);
let arrival_airport_lat = arrival_airport.lat.unwrap_or(0.0);
let arrival_airport_long = arrival_airport.lng.unwrap_or(0.0);
let progress = flight_progess(
aicraft_lat,
aicraft_long,
source_airport_lat,
source_airport_long,
arrival_airport_lat,
arrival_airport_long,
);

let speed = (flight.speed.unwrap_or(0) as f64 * 0.5399568) as i64;
let altitude = (flight.alt.unwrap_or(0) as f64 * 3.28084) as i64;
let timestamp = chrono::Utc::now();
let dep_time_display = format_time("Departure Time", &flight.dep_time, &flight.dep_estimated);
let arr_time_display = format_time("Arrival Time", &flight.arr_time, &flight.arr_estimated);
let airline = flight.airline_name.clone().unwrap_or_else(|| "Unknown".to_string());
let status = flight.status.clone().unwrap_or_else(|| "Unknown".to_string());
let duration = minutes_to_hours(flight.duration);
let progress_bar = progress_bar(progress);

let embed = CreateEmbed::new()
let mut embed = CreateEmbed::new()
.title(format!("Flight {}", flight_label))
.url("https://airlabs.co/api/")
.field("Airline", airline, true)
.field("Status", status, true)
.field("\u{200B}", "\u{200B}", true)
.field("Route", format!("{} -> {}", dep_airport, arr_airport), true)
.field("Status", &status, true)
.field("Route", format!("{} -> {}", dep_code, arr_code), true)
.field("\u{200B}", "\u{200B}", true)
.field("Duration", duration, true)
.field("🛫 Departure", dep_time_display, false)
.field("🛬 Arrival", arr_time_display, false)
.field("🛬 Arrival", arr_time_display, false);

if status == "en-route"{
embed = embed
.field("Speed", speed.to_string() + "kts", true)
.field("\u{200B}", "\u{200B}", true)
.field("Altitude", altitude.to_string() + "ft", true)
.field("Progress", format!("{} {} {}", &dep_code, progress_bar, &arr_code), false)
.timestamp(timestamp)
.footer(CreateEmbedFooter::new("Data provided by AirLabs API"))
.color(BLUE);
}
else{
embed = embed.timestamp(timestamp)
.footer(CreateEmbedFooter::new("Data provided by AirLabs API"))
.color(BLUE);
}

ctx.send(CreateReply::default().embed(embed)).await?;

Expand Down
27 changes: 27 additions & 0 deletions rustc-ice-2025-11-27T19_34_46-50762.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
thread 'rustc' panicked at /rustc-dev/f04e3dfc87d7e2b6ad53e7a52253812cd62eba50/compiler/rustc_codegen_ssa/src/back/write.rs:2035:29:
failed to open bitcode file `/Users/willmetz/Documents/KingfisherDiscordBot/uofu-cs-discord-bot/target/debug/incremental/bot_lib-1jp3ofeu98jcr/s-hddreoc2ln-17245bl-working/cc91yji1zlfzjcx6um0u89mv3.pre-lto.bc`: No such file or directory (os error 2)
stack backtrace:
0: 0x1103c8470 - std::backtrace::Backtrace::create::hcdc90ea6ac513737
1: 0x10e3e4fe0 - std[dfb9f133ee6e191]::panicking::update_hook::<alloc[591710c1154bb51]::boxed::Box<rustc_driver_impl[59af9e2f916ad0a9]::install_ice_hook::{closure#1}>>::{closure#0}
2: 0x1103c939c - std::panicking::panic_with_hook::h690f7dc4c3c0467b
3: 0x1103c904c - std::panicking::panic_handler::{{closure}}::haad8031a248fa0b7
4: 0x1103c2708 - std::sys::backtrace::__rust_end_short_backtrace::hb3e7df5d261b3d2f
5: 0x1103a6be8 - __rustc[aa7cb4f7ebc7d8e9]::rust_begin_unwind
6: 0x113131510 - core::panicking::panic_fmt::h6e170a2b7fd3e280
7: 0x11317d680 - rustc_codegen_ssa[5f73025d5957b540]::back::write::submit_pre_lto_module_to_llvm::<rustc_codegen_llvm[99bf4beba468145f]::LlvmCodegenBackend>::{closure#0}
8: 0x10e034418 - rustc_codegen_ssa[5f73025d5957b540]::base::codegen_crate::<rustc_codegen_llvm[99bf4beba468145f]::LlvmCodegenBackend>
9: 0x10e12658c - <rustc_codegen_llvm[99bf4beba468145f]::LlvmCodegenBackend as rustc_codegen_ssa[5f73025d5957b540]::traits::backend::CodegenBackend>::codegen_crate
10: 0x10ecb6b40 - <rustc_interface[3d4ce27018f23967]::queries::Linker>::codegen_and_build_linker
11: 0x10e39bcc4 - rustc_interface[3d4ce27018f23967]::passes::create_and_enter_global_ctxt::<core[fa1b534b39e65350]::option::Option<rustc_interface[3d4ce27018f23967]::queries::Linker>, rustc_driver_impl[59af9e2f916ad0a9]::run_compiler::{closure#0}::{closure#2}>
12: 0x10e3e8fbc - rustc_interface[3d4ce27018f23967]::interface::run_compiler::<(), rustc_driver_impl[59af9e2f916ad0a9]::run_compiler::{closure#0}>::{closure#1}
13: 0x10e3d7c18 - std[dfb9f133ee6e191]::sys::backtrace::__rust_begin_short_backtrace::<rustc_interface[3d4ce27018f23967]::util::run_in_thread_with_globals<rustc_interface[3d4ce27018f23967]::util::run_in_thread_pool_with_globals<rustc_interface[3d4ce27018f23967]::interface::run_compiler<(), rustc_driver_impl[59af9e2f916ad0a9]::run_compiler::{closure#0}>::{closure#1}, ()>::{closure#0}, ()>::{closure#0}::{closure#0}, ()>
14: 0x10e3ed0d0 - <<std[dfb9f133ee6e191]::thread::Builder>::spawn_unchecked_<rustc_interface[3d4ce27018f23967]::util::run_in_thread_with_globals<rustc_interface[3d4ce27018f23967]::util::run_in_thread_pool_with_globals<rustc_interface[3d4ce27018f23967]::interface::run_compiler<(), rustc_driver_impl[59af9e2f916ad0a9]::run_compiler::{closure#0}>::{closure#1}, ()>::{closure#0}, ()>::{closure#0}::{closure#0}, ()>::{closure#1} as core[fa1b534b39e65350]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0}
15: 0x1103bf1d8 - std::sys::thread::unix::Thread::new::thread_start::h0e4da6e8edf74dde
16: 0x1884bdc08 - __pthread_cond_wait


rustc version: 1.92.0-nightly (f04e3dfc8 2025-10-19)
platform: aarch64-apple-darwin

query stack during panic:
end of query stack