mirror of
https://github.com/FliegendeWurst/telegram_notes_bot.git
synced 2024-09-07 21:21:22 +00:00
Print debug info for files and attempt to parse iCalendar files
This commit is contained in:
parent
da90479eda
commit
ec00812887
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1129,6 +1129,7 @@ dependencies = [
|
||||
"futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ical 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"reqwest 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2,6 +2,7 @@
|
||||
name = "telegram_notes_bot"
|
||||
version = "0.1.0"
|
||||
authors = ["FliegendeWurst <2012gdwu@web.de>"]
|
||||
license = "GPL-3.0+"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
@ -18,3 +19,4 @@ once_cell = "1.3.1"
|
||||
thiserror = "1.0.15"
|
||||
serde_json = "1.0.51"
|
||||
ical = "0.6.0"
|
||||
mime = "0.3.16"
|
||||
|
116
src/ical_parsing.rs
Normal file
116
src/ical_parsing.rs
Normal file
@ -0,0 +1,116 @@
|
||||
use chrono::{Duration, NaiveDateTime, NaiveDate};
|
||||
use ical::parser::ical::component::IcalEvent;
|
||||
use ical::parser::ical::IcalParser;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Calendar {
|
||||
pub name: String,
|
||||
pub events: Vec<Event>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Event {
|
||||
pub uid: String,
|
||||
pub summary: String,
|
||||
pub description: String,
|
||||
pub start: NaiveDateTime,
|
||||
pub end: NaiveDateTime,
|
||||
pub duration: Option<Duration>,
|
||||
pub location: String,
|
||||
}
|
||||
|
||||
pub fn parse_calendar(data: &str) -> Result<Calendar, Error> {
|
||||
let cal = IcalParser::new(data.as_bytes()).next().ok_or(Error::Nothing)??;
|
||||
let mut name = None;
|
||||
let mut events = Vec::new();
|
||||
for prop in cal.properties {
|
||||
match prop.name.as_ref() {
|
||||
"NAME" => name = Some(prop.value.unwrap_or_default()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
for event in cal.events {
|
||||
events.push(process_event(event)?);
|
||||
}
|
||||
let name = name.unwrap_or_default();
|
||||
Ok(Calendar {
|
||||
name, events
|
||||
})
|
||||
}
|
||||
|
||||
fn process_event(event: IcalEvent) -> Result<Event, Error> {
|
||||
let mut uid = None;
|
||||
let mut summary = None;
|
||||
let mut description = None;
|
||||
let mut start = None;
|
||||
let mut end = None;
|
||||
let mut duration = None;
|
||||
let mut location = None;
|
||||
for prop in event.properties {
|
||||
let value = prop.value.unwrap_or_default();
|
||||
match prop.name.as_ref() {
|
||||
"UID" => uid = Some(value),
|
||||
"SUMMARY" => summary = Some(value),
|
||||
"LOCATION" => location = Some(value),
|
||||
"DESCRIPTION" => description = Some(value),
|
||||
"STATUS" => { /* TODO: status */ },
|
||||
"DTSTART" => start = Some(process_dt(&value)?),
|
||||
"DTEND" => end = Some(process_dt(&value)?),
|
||||
"DURATION" => duration = Some(process_duration(&value)?),
|
||||
"RRULE" => { /* TODO: periodic */ },
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
// TODO: don't put defaults here
|
||||
let start = start.ok_or(Error::Data("no dtstart"))?;
|
||||
let end = end.ok_or(Error::Data("no dtend"))?;
|
||||
Ok(Event {
|
||||
uid: uid.unwrap_or_default(),
|
||||
summary: summary.unwrap_or_default(),
|
||||
description: description.unwrap_or_default(),
|
||||
start: start,
|
||||
end: end,
|
||||
duration,
|
||||
location: location.unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
|
||||
fn process_dt(value: &str) -> Result<NaiveDateTime, Error> {
|
||||
// 20200626T140000
|
||||
if value.len() != 15 {
|
||||
return Err(Error::Data("invalid dt length"));
|
||||
}
|
||||
// TODO: error handling
|
||||
let year = value[0..4].parse()?;
|
||||
let month = value[4..6].parse()?;
|
||||
let day = value[6..8].parse()?;
|
||||
let hour = value[9..11].parse()?;
|
||||
let minute = value[11..13].parse()?;
|
||||
let second = value[13..15].parse()?;
|
||||
|
||||
Ok(NaiveDate::from_ymd(year, month, day).and_hms(hour, minute, second))
|
||||
}
|
||||
|
||||
fn process_duration(_value: &str) -> Result<Duration, Error> {
|
||||
// TODO
|
||||
return Err(Error::Data("duration parsing not implemented"));
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("parsing error: {0}")]
|
||||
Ical(ical::parser::ParserError),
|
||||
#[error("data error: {0}")]
|
||||
Data(&'static str),
|
||||
#[error("parse error: {0}")]
|
||||
IntegerParsing(#[from] std::num::ParseIntError),
|
||||
#[error("no calendar found")]
|
||||
Nothing
|
||||
}
|
||||
|
||||
impl From<ical::parser::ParserError> for Error {
|
||||
fn from(x: ical::parser::ParserError) -> Self {
|
||||
Error::Ical(x)
|
||||
}
|
||||
}
|
43
src/main.rs
43
src/main.rs
@ -1,6 +1,7 @@
|
||||
use chrono::prelude::*;
|
||||
use futures_util::stream::StreamExt;
|
||||
use maplit::hashmap;
|
||||
use mime::Mime;
|
||||
use once_cell::sync::Lazy;
|
||||
use reqwest::Client;
|
||||
use serde_derive::Deserialize;
|
||||
@ -16,10 +17,15 @@ use std::env;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
mod ical_parsing;
|
||||
|
||||
static TELEGRAM_BOT_TOKEN: Lazy<String> = Lazy::new(|| {
|
||||
env::var("TELEGRAM_BOT_TOKEN").expect("TELEGRAM_BOT_TOKEN not set")
|
||||
});
|
||||
|
||||
static API: Lazy<Arc<Api>> = Lazy::new(|| {
|
||||
let telegram_token = env::var("TELEGRAM_BOT_TOKEN").expect("TELEGRAM_BOT_TOKEN not set");
|
||||
println!("Initializing Telegram API..");
|
||||
Arc::new(Api::new(telegram_token))
|
||||
Arc::new(Api::new(&*TELEGRAM_BOT_TOKEN))
|
||||
});
|
||||
|
||||
static TRILIUM_TOKEN: Lazy<String> = Lazy::new(|| {
|
||||
@ -122,6 +128,24 @@ async fn process_one(update: Update, reminder_msg: &mut MessageId, reminder_text
|
||||
} else {
|
||||
API.send(message.text_reply("Text saved :-)")).await?;
|
||||
}
|
||||
} else if let MessageKind::Document { ref data, ref caption, .. } = message.kind {
|
||||
let document = data;
|
||||
send_message(format!("Document {:?} {:?} {:?} {:?}", caption, document.file_id, document.file_name, document.mime_type)).await?;
|
||||
let get_file = GetFile::new(&document);
|
||||
let file = API.send(get_file).await?;
|
||||
let url = file.get_url(&TELEGRAM_BOT_TOKEN).ok_or_else(|| error("url is none"))?;
|
||||
let data = CLIENT.get(&url).send().await?.bytes().await?;
|
||||
let mime: Mime = document.mime_type.as_ref().ok_or_else(|| error("no mime type"))?.parse()?;
|
||||
match (mime.type_(), mime.subtype()) {
|
||||
(mime::TEXT, x) if x == "calendar" => {
|
||||
let text = String::from_utf8_lossy(&data);
|
||||
let text = text.replace("\n<", "<"); // newlines in HTML values
|
||||
send_message(&text).await?;
|
||||
let calendar = ical_parsing::parse_calendar(&text)?;
|
||||
send_message(format!("{:?}", calendar)).await?;
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
} else if let UpdateKind::CallbackQuery(cb) = update.kind {
|
||||
match &*cb.data.unwrap_or_default() {
|
||||
@ -278,6 +302,11 @@ async fn notify_owner_impl(time_left: &str, task: Task) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn send_message<S: Into<String>>(msg: S) -> Result<(), Error> {
|
||||
API.send(SendMessage::new(*OWNER, msg.into())).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[allow(non_snake_case)]
|
||||
struct Task {
|
||||
@ -323,4 +352,14 @@ pub enum Error {
|
||||
Network(#[from] reqwest::Error),
|
||||
#[error("telegram error: {0}")]
|
||||
Telegram(#[from] telegram_bot::Error),
|
||||
#[error("mime parsing error: {0}")]
|
||||
Mime(#[from] mime::FromStrError),
|
||||
#[error("ical parsing error: {0}")]
|
||||
Ical(#[from] ical_parsing::Error),
|
||||
#[error("internal error: {0}")]
|
||||
CustomMessage(String),
|
||||
}
|
||||
|
||||
fn error<S: Into<String>>(msg: S) -> Error {
|
||||
Error::CustomMessage(msg.into())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user