mirror of
https://github.com/jhbruhn/ics-adapter.git
synced 2025-03-14 19:15:50 +00:00
Update image with new functionality for OEPL days and all day events
This commit is contained in:
parent
0de6b8391f
commit
2a2821c5b2
4 changed files with 78 additions and 24 deletions
30
Cargo.lock
generated
30
Cargo.lock
generated
|
@ -96,6 +96,7 @@ dependencies = [
|
|||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"time",
|
||||
"wasm-bindgen",
|
||||
"winapi",
|
||||
]
|
||||
|
@ -296,7 +297,7 @@ checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -476,10 +477,12 @@ name = "ics-adapter"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"icalendar",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"tokio",
|
||||
"urlencoding",
|
||||
"warp",
|
||||
]
|
||||
|
||||
|
@ -622,7 +625,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
|
@ -1198,6 +1201,17 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
|
@ -1389,6 +1403,12 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urlencoding"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||
|
||||
[[package]]
|
||||
name = "utf-8"
|
||||
version = "0.7.6"
|
||||
|
@ -1457,6 +1477,12 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
|
|
|
@ -12,3 +12,5 @@ reqwest = { version = "0.11", features = ["native-tls-vendored"] }
|
|||
anyhow = "1"
|
||||
icalendar = { version = "0.15", features = ["chrono-tz"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
urlencoding = "2"
|
||||
chrono = "0"
|
||||
|
|
14
README.md
14
README.md
|
@ -16,6 +16,20 @@ services:
|
|||
|
||||
As you can see, you only have to forward port 3000 to wherever you like, in this case port 3000.
|
||||
The URL you have to enter into the OEPL Google Calendar config then looks like this:
|
||||
|
||||
```
|
||||
http://<docker-host>:3000/calendar/<ics-url>/entries
|
||||
```
|
||||
|
||||
It is important that you urlencode the `<ics-url>` with a tool like this: https://www.urlencoder.org/
|
||||
|
||||
With an actual .ics url, it looks like this:
|
||||
```
|
||||
http://192.168.178.42:3000/calendar/https%3A%2F%2Fnextcloud.net%2Fremote.php%2Fdav%2Fpublic-calendars%2Fasdlkijf/entries
|
||||
```
|
||||
|
||||
Previous versions also supported these URL formats, but these are not compatible with modern OEPL features:
|
||||
|
||||
```
|
||||
http://<docker-host>:3000/get?url=<ics-url>
|
||||
```
|
||||
|
|
56
src/main.rs
56
src/main.rs
|
@ -19,11 +19,15 @@ fn now_timestamp_secs() -> i64 {
|
|||
|
||||
async fn handler(params: HashMap<String, String>) -> Result<impl Reply, Rejection> {
|
||||
match params.get("url") {
|
||||
Some(url) => Ok(warp::reply::json(&convert(&url).await.map_err(|_| warp::reject::reject())?.entries)),
|
||||
Some(url) => Ok(warp::reply::json(&convert(&url, params.get("days")).await.map_err(|_| warp::reject::reject())?.entries)),
|
||||
None => Err(warp::reject::reject())
|
||||
}
|
||||
}
|
||||
|
||||
async fn new_handler(url: String, params: HashMap<String, String>) -> Result<impl Reply, Rejection> {
|
||||
Ok(warp::reply::json(&convert(&url, params.get("days")).await.map_err(|_| warp::reject::reject())?.entries))
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct CustomCalendar {
|
||||
entries: Vec<CustomCalendarEntry>,
|
||||
|
@ -36,9 +40,11 @@ struct CustomCalendarEntry {
|
|||
end: i64,
|
||||
location: String,
|
||||
description: String,
|
||||
isallday: bool,
|
||||
}
|
||||
|
||||
async fn convert(url: &str) -> Result<CustomCalendar> {
|
||||
async fn convert(url: &str, days: Option<&String>) -> Result<CustomCalendar> {
|
||||
let url = urlencoding::decode(url)?.into_owned();
|
||||
let ics_text = reqwest::get(url)
|
||||
.await?
|
||||
.text()
|
||||
|
@ -49,7 +55,7 @@ async fn convert(url: &str) -> Result<CustomCalendar> {
|
|||
let mut entries = Vec::new();
|
||||
|
||||
let filter_start = now_timestamp_secs();
|
||||
let filter_end = now_timestamp_secs() + (24 * 60 * 60);
|
||||
let filter_end = now_timestamp_secs() + (24 * 60 * 60) * days.unwrap_or(&String::from("1")).parse().unwrap_or(1) as i64;
|
||||
|
||||
for event in calendar.components {
|
||||
if let Some(event) = event.as_event() {
|
||||
|
@ -57,10 +63,6 @@ async fn convert(url: &str) -> Result<CustomCalendar> {
|
|||
println!("No start!");
|
||||
continue;
|
||||
};
|
||||
let Some(end) = event.get_end() else {
|
||||
println!("No end!");
|
||||
continue;
|
||||
};
|
||||
|
||||
let start = match convert_time(start) {
|
||||
Ok(t) => { t },
|
||||
|
@ -70,23 +72,30 @@ async fn convert(url: &str) -> Result<CustomCalendar> {
|
|||
}
|
||||
};
|
||||
|
||||
let end = match convert_time(end) {
|
||||
Ok(t) => { t },
|
||||
Err(e) => {
|
||||
println!("Invalid end timestamp: {:?}", e);
|
||||
continue;
|
||||
}
|
||||
let end = match event.get_end() {
|
||||
Some(end) => {
|
||||
match convert_time(end) {
|
||||
Ok(t) => { t },
|
||||
Err(e) => {
|
||||
println!("Invalid end timestamp: {:?}", e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
},
|
||||
None => start + chrono::Duration::days(1),
|
||||
};
|
||||
|
||||
if start < filter_start || start > filter_end {
|
||||
if start.timestamp() < filter_start || start.timestamp() > filter_end {
|
||||
continue;
|
||||
}
|
||||
entries.push(CustomCalendarEntry {
|
||||
title: event.get_summary().unwrap_or("").to_string(),
|
||||
description: event.get_description().unwrap_or("").to_string(),
|
||||
location: event.get_location().unwrap_or("").to_string(),
|
||||
start,
|
||||
end
|
||||
start: start.timestamp(),
|
||||
end: end.timestamp(),
|
||||
isallday: start.time() == chrono::NaiveTime::from_hms_opt(0, 0, 0).unwrap() && end - start == chrono::Duration::days(1) // the event has a length of 24 hours and
|
||||
// starts at 00:00
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +103,7 @@ async fn convert(url: &str) -> Result<CustomCalendar> {
|
|||
Ok(CustomCalendar{entries})
|
||||
}
|
||||
|
||||
fn convert_time(dt: icalendar::DatePerhapsTime) -> Result<i64> {
|
||||
fn convert_time(dt: icalendar::DatePerhapsTime) -> Result<chrono::DateTime<chrono::Utc>> {
|
||||
Ok(match dt {
|
||||
icalendar::DatePerhapsTime::DateTime(cdt) => {
|
||||
let cdt = match cdt {
|
||||
|
@ -106,20 +115,23 @@ fn convert_time(dt: icalendar::DatePerhapsTime) -> Result<i64> {
|
|||
},
|
||||
_ => cdt,
|
||||
};
|
||||
cdt.try_into_utc().ok_or(anyhow::Error::msg("failed to convert to utc"))?.timestamp()
|
||||
cdt.try_into_utc().ok_or(anyhow::Error::msg("failed to convert to utc"))?
|
||||
},
|
||||
icalendar::DatePerhapsTime::Date(nd) => nd.and_hms_opt(0, 0, 0).unwrap().timestamp(),
|
||||
icalendar::DatePerhapsTime::Date(nd) => nd.and_hms_opt(0, 0, 0).unwrap().and_utc(),
|
||||
})
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let converter = warp::get()
|
||||
.and(warp::path("get"))
|
||||
let converter = warp::path("get")
|
||||
.and(warp::query::<HashMap<String, String>>())
|
||||
.and_then(handler);
|
||||
|
||||
warp::serve(converter)
|
||||
let new_converter = warp::path!("calendar" / String / "entries")
|
||||
.and(warp::query::<HashMap<String, String>>())
|
||||
.and_then(new_handler);
|
||||
|
||||
warp::serve(warp::get().and(converter.or(new_converter)))
|
||||
.run(([0, 0, 0, 0], 3000))
|
||||
.await
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue