Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. use core::time;
  2. use std::collections::*;
  3. use std::path::PathBuf;
  4. use std::sync::Arc;
  5. use futures::compat::Stream01CompatExt;
  6. use futures::prelude::*;
  7. use hyper::client::connect::dns::GaiResolver;
  8. use hyper::client::connect::HttpConnector;
  9. use hyper_tls::HttpsConnector;
  10. use inotify::ffi::*;
  11. use tokio::fs as tokiofs;
  12. use tokio::prelude::*;
  13. use tokio::sync::mpsc;
  14. use tokio_inotify;
  15. use url::Url;
  16. use ruma_client::Client;
  17. use ruma_client_api::r0::message as rumamessage;
  18. use ruma_events::{self, room::message::*};
  19. use ruma_identifiers::RoomId;
  20. use crate::config::{self, *};
  21. use crate::sender;
  22. type MatrixClient = Client<HttpsConnector<HttpConnector<GaiResolver>>>;
  23. type MessageRequest = rumamessage::create_message_event::Request;
  24. #[derive(Debug)]
  25. pub enum Error {
  26. FileFormatMismatch,
  27. BadUrl,
  28. UnspecifiedClient(String),
  29. Io(io::Error),
  30. MtxClient(ruma_client::Error),
  31. }
  32. impl From<io::Error> for Error {
  33. fn from(f: io::Error) -> Self {
  34. Self::Io(f)
  35. }
  36. }
  37. impl From<ruma_client::Error> for Error {
  38. fn from(f: ruma_client::Error) -> Self {
  39. Self::MtxClient(f)
  40. }
  41. }
  42. #[derive(Clone)]
  43. struct SpoolAction {
  44. client: Arc<MatrixClient>,
  45. room: RoomId,
  46. delay_secs: u32,
  47. watch_path: PathBuf,
  48. }
  49. pub async fn start_spoolers(
  50. conf: Config,
  51. client_chans: HashMap<String, mpsc::Sender<sender::Message>>,
  52. ) -> Result<(), Error> {
  53. for sd in conf.spool_dirs {
  54. let chan = client_chans
  55. .get(&sd.sender_acct_label)
  56. .ok_or_else(|| Error::UnspecifiedClient(sd.sender_acct_label.clone()))?;
  57. let ain = tokio_inotify::AsyncINotify::init().expect("spool: inotify init");
  58. let w = ain.add_watch(&sd.path, IN_CLOSE_WRITE | IN_MOVED_TO)?;
  59. println!("[spool] added watch: {:?}", sd.path);
  60. tokio::spawn(do_watch_dir(ain, sd, chan.clone()));
  61. }
  62. Ok(())
  63. }
  64. async fn do_watch_dir(
  65. inot: tokio_inotify::AsyncINotify,
  66. sdc: config::SpoolDir,
  67. mut dest: mpsc::Sender<sender::Message>,
  68. ) {
  69. let mut iter = inot.compat();
  70. while let Some(ent) = iter.next().await {
  71. match ent {
  72. Ok(ent) => {
  73. // Just succ up the file and send it over. We'll do the
  74. // formatting later.
  75. let mut real_path = sdc.path.clone();
  76. real_path.push(ent.name);
  77. let s = match file_as_string(&real_path).await {
  78. Ok(s) => s,
  79. Err(e) => {
  80. eprintln!(
  81. "[spool] warning, could not read file, ignoring: {:?}",
  82. real_path
  83. );
  84. continue;
  85. }
  86. };
  87. let msg =
  88. sender::Message::new_delay(sdc.dest_room_id.clone(), s, sdc.send_delay_sec);
  89. let tout = time::Duration::from_secs(30);
  90. dest.send_timeout(msg, tout)
  91. .map_err(|_| ())
  92. .await
  93. .expect("spool: relay channel send timeout");
  94. if let Err(e) = tokiofs::remove_file(&real_path).await {
  95. eprintln!(
  96. "[spool] warning: could not remove sent file, ignoring: {:?}",
  97. real_path
  98. );
  99. }
  100. }
  101. Err(e) => panic!("spool: error reading watch {:?}", e),
  102. }
  103. }
  104. }
  105. /*
  106. async fn process_file(p: &PathBuf, sa: &SpoolAction) -> Result<(), Error> {
  107. let ext = match p.extension().map(|e| e.to_str()).flatten() {
  108. Some(v) => v,
  109. None => {
  110. eprintln!("Found weird file {:?}, ignoring", p);
  111. return Ok(());
  112. }
  113. };
  114. let name = p
  115. .file_name()
  116. .map(|e| e.to_str())
  117. .flatten()
  118. .unwrap_or("[non-UTF-8]");
  119. // This makes me *mad*.
  120. let mut real_path = sa.watch_path.clone();
  121. real_path.push(p);
  122. match ext {
  123. "txt" => {
  124. println!("Processing file for {} at {:?}", sa.room, p);
  125. let buf = match file_as_string(&real_path).await {
  126. Ok(v) => v,
  127. Err(Error::FileFormatMismatch) => {
  128. println!("File {} is not UTF-8, ignoring", name);
  129. return Ok(());
  130. }
  131. Err(e) => return Err(e),
  132. };
  133. let mut rng = rand::thread_rng();
  134. let req = make_text_request(sa.room.clone(), buf.as_str(), &mut rng);
  135. match sa.client.as_ref().request(req).await {
  136. Ok(_) => {
  137. // Now delete it if it passed.
  138. }
  139. Err(e) => println!("Error processing {}: {:?}", name, e),
  140. }
  141. }
  142. _ => println!(
  143. "Found file {:?}, but it has unsupported extension \"{}\"",
  144. p, ext
  145. ),
  146. }
  147. Ok(())
  148. }*/
  149. async fn file_as_string(p: &PathBuf) -> Result<String, Error> {
  150. let mut buf = Vec::new();
  151. let mut f = tokiofs::File::open(p).await?;
  152. f.read_to_end(&mut buf).await?;
  153. String::from_utf8(buf).map_err(|_| Error::FileFormatMismatch)
  154. }