matrix.rs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. use std::fmt;
  2. use matrix_sdk::{
  3. self,
  4. config::SyncSettings,
  5. ruma::{
  6. api::client::session::{
  7. get_login_types::v3::{IdentityProvider, LoginType},
  8. login,
  9. },
  10. device_id,
  11. events::room::message::RoomMessageEventContent,
  12. OwnedUserId, RoomId, UserId,
  13. },
  14. Client, Session,
  15. };
  16. use serde::{Deserialize, Serialize};
  17. use sqlx::{pool::PoolConnection, postgres::PgQueryResult, Postgres};
  18. use url::Url;
  19. use crate::{
  20. board::Board, entity::{Entity, Contact}, file::File, milestone::Milestone, project::Project, task::Task, note::Note,
  21. };
  22. // authentication, messaging and server management stuff for matrix.
  23. const INITIAL_DEVICE_DISPLAY_NAME: &str = "Kinbrio-client";
  24. pub struct Choice {
  25. pub url: String,
  26. pub display: String,
  27. pub logo: String,
  28. }
  29. pub async fn get_login_urls(
  30. homeserver_url: String,
  31. redirect_url: String,
  32. ) -> Result<Vec<Choice>, matrix_sdk::Error> {
  33. let homeserver_url = Url::parse(&homeserver_url).expect("Url Correct");
  34. let client = Client::new(homeserver_url)
  35. .await
  36. .expect("Matrix Server Connecting");
  37. let mut choices = Vec::new();
  38. let login_types = client
  39. .get_login_types()
  40. .await
  41. .expect("Login types found")
  42. .flows;
  43. for login_type in login_types {
  44. match login_type {
  45. LoginType::Sso(sso) => {
  46. if sso.identity_providers.is_empty() {
  47. choices.push(LoginChoice::Sso)
  48. } else {
  49. choices.extend(sso.identity_providers.into_iter().map(LoginChoice::SsoIdp))
  50. }
  51. }
  52. LoginType::Password(t) => {
  53. choices.push(LoginChoice::Password)
  54. }
  55. LoginType::Token(_) | _ => {}
  56. }
  57. }
  58. let mut urls = vec![];
  59. for c in &choices {
  60. let u = c
  61. .login(&client, redirect_url.clone())
  62. .await
  63. .expect("login URL fails");
  64. urls.push(Choice {
  65. url: u.clone(),
  66. display: c.to_string(),
  67. logo: c.get_icon(),
  68. });
  69. }
  70. return Ok(urls);
  71. }
  72. #[derive(Debug)]
  73. pub enum LoginChoice {
  74. Password,
  75. /// Login with SSO.
  76. Sso,
  77. /// Login with a specific SSO identity provider.
  78. SsoIdp(IdentityProvider),
  79. }
  80. impl LoginChoice {
  81. /// Login with this login choice.
  82. async fn login(&self, client: &Client, redirect: String) -> anyhow::Result<String> {
  83. match self {
  84. LoginChoice::Password => login_with_password_url(client),
  85. LoginChoice::Sso => login_with_sso_url(client, redirect, None).await,
  86. LoginChoice::SsoIdp(idp) => login_with_sso_url(client, redirect, Some(idp)).await,
  87. }
  88. }
  89. fn get_icon_mxc(&self) -> String {
  90. match self {
  91. LoginChoice::Password => "/fs/images/sso/user_password.svg".to_string(),
  92. LoginChoice::Sso => "hmm".to_string(),
  93. LoginChoice::SsoIdp(idp) => idp.icon.as_ref().expect("get icon URL").to_string(),
  94. }
  95. }
  96. fn get_icon(&self) -> String {
  97. match self {
  98. LoginChoice::Password => "/fs/images/sso/user_password.svg".to_string(),
  99. LoginChoice::Sso => "hmm".to_string(),
  100. LoginChoice::SsoIdp(idp) => {
  101. let mxc_uri = idp.icon.as_ref().expect("get icon URL").to_string();
  102. mxc_uri.replace("mxc://", "https://matrix.org/_matrix/media/r0/download/")
  103. },
  104. }
  105. }
  106. }
  107. impl fmt::Display for LoginChoice {
  108. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  109. match self {
  110. LoginChoice::Password => write!(f, "Username and password"),
  111. LoginChoice::Sso => write!(f, "SSO"),
  112. LoginChoice::SsoIdp(idp) => write!(f, "{}", idp.name),
  113. }
  114. }
  115. }
  116. /// Login with a username and password.
  117. fn login_with_password_url(client: &Client) -> anyhow::Result<String> {
  118. return Ok("/login_by_username".to_string())
  119. }
  120. /// Login with SSO.
  121. pub(crate) async fn restore_from_session(
  122. homeserver_url: String,
  123. session: Session,
  124. ) -> anyhow::Result<()> {
  125. let homeserver_url = Url::parse(&homeserver_url).expect("URL parsing");
  126. let client = Client::new(homeserver_url)
  127. .await
  128. .expect("Matrix server connection");
  129. client.restore_login(session).await.expect("Restore login");
  130. Ok(())
  131. }
  132. pub(crate) async fn login_with_password(
  133. homeserver_url: String,
  134. uid: String,
  135. password: String,
  136. ) -> anyhow::Result<login::v3::Response> {
  137. let homeserver_url = Url::parse(&homeserver_url).expect("URL parsing");
  138. let client = Client::new(homeserver_url)
  139. .await
  140. .expect("Matrix server connection");
  141. let login_builder = client.login_username(uid.as_str(), password.as_str()).send().await.expect("logged in");
  142. Ok(login_builder)
  143. }
  144. pub(crate) async fn account(
  145. homeserver_url: String,
  146. token: String,
  147. user_id:OwnedUserId,
  148. ) -> (std::string::String, std::string::String, std::string::String) {
  149. let homeserver_url = Url::parse(&homeserver_url).expect("URL parsing");
  150. let client = Client::new(homeserver_url)
  151. .await
  152. .expect("Matrix server connection");
  153. client.restore_login(Session {
  154. access_token: token,
  155. refresh_token: None,
  156. user_id: OwnedUserId::from(user_id),
  157. device_id: device_id!("kinbrio").to_owned(),
  158. }).await.expect("Restore");
  159. let avatar = client.account().get_avatar_url().await.expect("Get avatar URL").expect("Unroll").to_string();
  160. let threepids = client.account().get_3pids().await.expect("Get 3pid").threepids;
  161. let mut addy = String::default();
  162. for pid in threepids {
  163. addy = match pid.medium {
  164. matrix_sdk::ruma::thirdparty::Medium::Email => pid.address,
  165. matrix_sdk::ruma::thirdparty::Medium::Msisdn => pid.address,
  166. _ => String::default(),
  167. };
  168. if addy.len() > 0 {
  169. break;
  170. }
  171. };
  172. let display_name = client.account().get_profile().await.expect("get prorfile").displayname.unwrap_or_default();
  173. (addy, avatar, display_name)
  174. }
  175. pub(crate) async fn login_with_token(
  176. homeserver_url: String,
  177. token: String,
  178. ) -> anyhow::Result<login::v3::Response> {
  179. let homeserver_url = Url::parse(&homeserver_url).expect("URL parsing");
  180. let client = Client::new(homeserver_url)
  181. .await
  182. .expect("Matrix server connection");
  183. let login_builder = client.login_token(&token).send().await?;
  184. Ok(login_builder)
  185. }
  186. async fn login_with_sso_url(
  187. client: &Client,
  188. redirect: String,
  189. idp: Option<&IdentityProvider>,
  190. ) -> anyhow::Result<String> {
  191. let login_builder = client
  192. .get_sso_login_url(&redirect, Some(&idp.expect("get provider").id))
  193. .await
  194. .expect("Get provider");
  195. Ok(login_builder)
  196. }
  197. pub async fn delete_room(
  198. conn: &mut PoolConnection<Postgres>,
  199. key: uuid::Uuid,
  200. ) -> Result<PgQueryResult, sqlx::Error> {
  201. return sqlx::query!("DELETE FROM rooms where key=$1", key)
  202. .execute(conn)
  203. .await;
  204. }
  205. #[derive(Debug, Deserialize, Serialize)]
  206. pub struct Room {
  207. pub key: uuid::Uuid,
  208. pub owner_key: uuid::Uuid,
  209. pub organization_key: uuid::Uuid,
  210. pub name: String,
  211. pub description: String,
  212. pub matrix_room_url: String,
  213. pub matrix_room_id: String,
  214. pub message_types: MessageDataType,
  215. pub alert_level: i16,
  216. pub created: i64,
  217. pub updated: i64,
  218. }
  219. pub async fn get_rooms(
  220. conn: &mut PoolConnection<Postgres>,
  221. organization_key: uuid::Uuid,
  222. ) -> Vec<Room> {
  223. let room_records = sqlx::query!(
  224. "select
  225. key,
  226. owner_key,
  227. organization_key,
  228. name,
  229. description,
  230. matrix_room_url,
  231. matrix_room_id,
  232. message_types,
  233. alert_level,
  234. created,
  235. updated from rooms
  236. where organization_key = $1",
  237. organization_key
  238. )
  239. .fetch_all(conn)
  240. .await
  241. .expect("Select room by key");
  242. let mut rooms = vec![];
  243. for room in room_records {
  244. let m_type: MessageDataType = room.message_types.expect("message_types exists").into();
  245. rooms.push(Room {
  246. key: room.key.expect("key exists"),
  247. owner_key: room.owner_key.expect("owner_key exists"),
  248. organization_key: room.organization_key.expect("organization_key exists"),
  249. name: room.name.expect("name exists"),
  250. description: room.description.expect("description exists"),
  251. matrix_room_url: room.matrix_room_url.expect("matrix_room_url exists"),
  252. matrix_room_id: room.matrix_room_id.expect("matrix_room_id exists"),
  253. message_types: m_type,
  254. alert_level: room.alert_level.expect("alert_level exists"),
  255. created: room.created.expect("created exists"),
  256. updated: room.updated.expect("updated exists"),
  257. });
  258. }
  259. rooms
  260. }
  261. pub async fn get_room(conn: &mut PoolConnection<Postgres>, key: uuid::Uuid) -> Room {
  262. let room = sqlx::query!(
  263. "select
  264. key,
  265. owner_key,
  266. organization_key,
  267. name,
  268. description,
  269. matrix_room_url,
  270. matrix_room_id,
  271. message_types,
  272. alert_level,
  273. created,
  274. updated from rooms
  275. where key = $1",
  276. key
  277. )
  278. .fetch_one(conn)
  279. .await
  280. .expect("Select room by key");
  281. Room {
  282. key: room.key.expect("key exists"),
  283. owner_key: room.owner_key.expect("owner_key exists"),
  284. organization_key: room.organization_key.expect("organization_key exists"),
  285. name: room.name.expect("name exists"),
  286. description: room.description.expect("description exists"),
  287. matrix_room_url: room.matrix_room_url.expect("matrix_room_url exists"),
  288. matrix_room_id: room.matrix_room_id.expect("matrix_room_id exists"),
  289. message_types: MessageDataType::All,
  290. alert_level: room.alert_level.expect("alert_level exists"),
  291. created: room.created.expect("created exists"),
  292. updated: room.updated.expect("updated exists"),
  293. }
  294. }
  295. pub async fn insert_room(conn: &mut PoolConnection<Postgres>, new_room: &Room) {
  296. let t = new_room.message_types as i16;
  297. sqlx::query!(
  298. "INSERT INTO rooms (
  299. key,
  300. owner_key,
  301. organization_key,
  302. name,
  303. description,
  304. matrix_room_url,
  305. matrix_room_id,
  306. message_types,
  307. alert_level,
  308. created,
  309. updated) values($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)",
  310. new_room.key,
  311. new_room.owner_key,
  312. new_room.organization_key,
  313. &new_room.name,
  314. &new_room.description,
  315. &new_room.matrix_room_url,
  316. &new_room.matrix_room_id,
  317. &t,
  318. &new_room.alert_level,
  319. new_room.created,
  320. new_room.updated,
  321. )
  322. .execute(conn)
  323. .await
  324. .expect("Insert Success");
  325. }
  326. #[derive(PartialEq, Debug, Deserialize, Serialize, Clone, Copy, sqlx::Type)]
  327. pub enum MessageDataType {
  328. All = 0,
  329. Board = 1,
  330. Entity = 2,
  331. File = 3,
  332. Milestone = 4,
  333. Oragnization = 5,
  334. Project = 6,
  335. Task = 7,
  336. User = 8,
  337. Report = 9,
  338. Room = 10,
  339. }
  340. impl Into<MessageDataType> for i16 {
  341. fn into(self) -> MessageDataType {
  342. match self {
  343. 0 => MessageDataType::All,
  344. 1 => MessageDataType::Board,
  345. 2 => MessageDataType::Entity,
  346. 3 => MessageDataType::File,
  347. 4 => MessageDataType::Milestone,
  348. 5 => MessageDataType::Oragnization,
  349. 6 => MessageDataType::Project,
  350. 7 => MessageDataType::Task,
  351. 8 => MessageDataType::User,
  352. 9 => MessageDataType::Report,
  353. 10 => MessageDataType::Room,
  354. _ => MessageDataType::All
  355. }
  356. }
  357. }
  358. impl From<MessageDataType> for i16 {
  359. fn from(t: MessageDataType) -> Self {
  360. match t {
  361. MessageDataType::All => 0,
  362. MessageDataType::Board => 1,
  363. MessageDataType::Entity => 2,
  364. MessageDataType::File => 3,
  365. MessageDataType::Milestone => 4,
  366. MessageDataType::Oragnization => 5,
  367. MessageDataType::Project => 6,
  368. MessageDataType::Task => 7,
  369. MessageDataType::User => 8,
  370. MessageDataType::Report => 9,
  371. MessageDataType::Room => 10
  372. }
  373. }
  374. }
  375. enum MessageActionType {
  376. All,
  377. Create,
  378. Update,
  379. Delete,
  380. Complete,
  381. }
  382. async fn message(
  383. conn: &mut PoolConnection<Postgres>,
  384. matrix_user_id: String,
  385. organization_id: uuid::Uuid,
  386. homeserver_url: String,
  387. token: String,
  388. data_type: MessageDataType,
  389. _action_type: MessageActionType,
  390. msg: String,
  391. ) -> Result<(), anyhow::Error> {
  392. let rooms = get_rooms(conn, organization_id).await;
  393. for room in rooms {
  394. if room.message_types == data_type {
  395. send_room_message(
  396. matrix_user_id.clone(),
  397. organization_id,
  398. homeserver_url.clone(),
  399. token.clone(),
  400. room.matrix_room_id.clone(),
  401. msg.clone(),
  402. ).await.expect("message sent");
  403. }
  404. }
  405. Ok(())
  406. }
  407. async fn send_room_message(
  408. matrix_user_id: String,
  409. _organization_id: uuid::Uuid,
  410. homeserver_url: String,
  411. token: String,
  412. room_id: String,
  413. msg: String,
  414. ) -> Result<(), anyhow::Error> {
  415. let user_id_str = matrix_user_id.to_string();
  416. let user_id = <&UserId>::try_from(user_id_str.as_str()).expect("parse user id");
  417. let homeserver_url =
  418. Url::parse(&homeserver_url).unwrap_or(Url::parse("https://matrix-client.matrix.org")?);
  419. let client = Client::new(homeserver_url).await?;
  420. let session = Session {
  421. access_token: token,
  422. refresh_token: None,
  423. user_id: OwnedUserId::from(user_id),
  424. device_id: device_id!("kinbrio").to_owned(),
  425. };
  426. client.restore_login(session).await.expect("Send login");
  427. client
  428. .sync_once(SyncSettings::default())
  429. .await
  430. .expect("sync");
  431. let room_id = <&RoomId>::try_from(room_id.as_str()).expect("parse room id");
  432. let room = client.get_joined_room(room_id).expect("Retrieve room");
  433. let content = RoomMessageEventContent::text_plain(msg);
  434. room.send(content, None).await.expect("Send room");
  435. Ok(())
  436. }
  437. pub(crate) async fn post_room_create(
  438. conn: &mut PoolConnection<Postgres>,
  439. homeserver_url: String,
  440. matrix_user_id: String,
  441. organization_id: uuid::Uuid,
  442. token: String,
  443. room: &Room,
  444. ) -> Result<(), anyhow::Error> {
  445. let msg = format!(
  446. "New Room 🚀 \n {} \n `{}`\n https://kinbrio.com/room/{}",
  447. room.name, room.description, room.key
  448. );
  449. message(
  450. conn,
  451. matrix_user_id,
  452. organization_id,
  453. homeserver_url,
  454. token,
  455. MessageDataType::Room,
  456. MessageActionType::Create,
  457. msg,
  458. )
  459. .await?;
  460. Ok(())
  461. }
  462. pub(crate) async fn post_file_create(
  463. conn: &mut PoolConnection<Postgres>,
  464. homeserver_url: String,
  465. matrix_user_id: String,
  466. organization_id: uuid::Uuid,
  467. token: String,
  468. file: &File,
  469. ) -> Result<(), anyhow::Error> {
  470. let msg = format!(
  471. "New File 🚀 \n {}\n `{}`\n https://kinbrio.com/file/{}",
  472. file.name, file.description, file.key
  473. );
  474. message(
  475. conn,
  476. matrix_user_id,
  477. organization_id,
  478. homeserver_url,
  479. token,
  480. MessageDataType::File,
  481. MessageActionType::Create,
  482. msg,
  483. )
  484. .await?;
  485. Ok(())
  486. }
  487. pub(crate) async fn post_milestone_create(
  488. conn: &mut PoolConnection<Postgres>,
  489. homeserver_url: String,
  490. matrix_user_id: String,
  491. organization_id: uuid::Uuid,
  492. token: String,
  493. milestone: &Milestone,
  494. ) -> Result<(), anyhow::Error> {
  495. let msg = format!(
  496. "New Milestone 🚀 \n {}\n `{}`\n https://kinbrio.com/milestone/{}",
  497. milestone.name, milestone.description, milestone.key
  498. );
  499. message(
  500. conn,
  501. matrix_user_id,
  502. organization_id,
  503. homeserver_url,
  504. token,
  505. MessageDataType::Milestone,
  506. MessageActionType::Create,
  507. msg,
  508. )
  509. .await?;
  510. Ok(())
  511. }
  512. pub(crate) async fn post_project_create(
  513. conn: &mut PoolConnection<Postgres>,
  514. homeserver_url: String,
  515. matrix_user_id: String,
  516. organization_id: uuid::Uuid,
  517. token: String,
  518. project: &Project,
  519. ) -> Result<(), anyhow::Error> {
  520. let msg = format!(
  521. "New Project 🚀 \n {}\n `{}`\n https://kinbrio.com/project/{}",
  522. project.name, project.description, project.key
  523. );
  524. message(
  525. conn,
  526. matrix_user_id,
  527. organization_id,
  528. homeserver_url,
  529. token,
  530. MessageDataType::Project,
  531. MessageActionType::Create,
  532. msg,
  533. )
  534. .await?;
  535. Ok(())
  536. }
  537. pub(crate) async fn post_entity_create(
  538. conn: &mut PoolConnection<Postgres>,
  539. homeserver_url: String,
  540. matrix_user_id: String,
  541. organization_id: uuid::Uuid,
  542. token: String,
  543. entity: &Entity,
  544. ) -> Result<(), anyhow::Error> {
  545. let msg = format!(
  546. "New Entity Added 🚀 \n {}\n `{}`\n https://kinbrio.com/entity/{}",
  547. entity.name, entity.description, entity.key
  548. );
  549. message(
  550. conn,
  551. matrix_user_id,
  552. organization_id,
  553. homeserver_url,
  554. token,
  555. MessageDataType::Entity,
  556. MessageActionType::Create,
  557. msg,
  558. )
  559. .await?;
  560. Ok(())
  561. }
  562. pub(crate) async fn post_note_create(
  563. conn: &mut PoolConnection<Postgres>,
  564. homeserver_url: String,
  565. matrix_user_id: String,
  566. organization_id: uuid::Uuid,
  567. token: String,
  568. note: &Note,
  569. ) -> Result<(), anyhow::Error> {
  570. let msg = format!(
  571. "New Note Added 🚀 \n {} \n https://kinbrio.com/contact/{}",
  572. note.title, note.key
  573. );
  574. message(
  575. conn,
  576. matrix_user_id,
  577. organization_id,
  578. homeserver_url,
  579. token,
  580. MessageDataType::Entity,
  581. MessageActionType::Create,
  582. msg,
  583. )
  584. .await?;
  585. Ok(())
  586. }
  587. pub(crate) async fn post_contact_create(
  588. conn: &mut PoolConnection<Postgres>,
  589. homeserver_url: String,
  590. matrix_user_id: String,
  591. organization_id: uuid::Uuid,
  592. token: String,
  593. contact: &Contact,
  594. ) -> Result<(), anyhow::Error> {
  595. let msg: String = format!(
  596. "New Contact Added 🚀 \n {} {}\n https://kinbrio.com/contact/{}",
  597. contact.first_name, contact.last_name, contact.key
  598. );
  599. message(
  600. conn,
  601. matrix_user_id,
  602. organization_id,
  603. homeserver_url,
  604. token,
  605. MessageDataType::Entity,
  606. MessageActionType::Create,
  607. msg,
  608. )
  609. .await?;
  610. Ok(())
  611. }
  612. pub(crate) async fn post_board_create(
  613. conn: &mut PoolConnection<Postgres>,
  614. homeserver_url: String,
  615. matrix_user_id: String,
  616. organization_id: uuid::Uuid,
  617. token: String,
  618. board: &Board,
  619. ) -> Result<(), anyhow::Error> {
  620. let msg = format!(
  621. "New Board 🚀 \n {}\n `{}`\n https://kinbrio.com/board/{}",
  622. board.name, board.description, board.key
  623. );
  624. message(
  625. conn,
  626. matrix_user_id,
  627. organization_id,
  628. homeserver_url,
  629. token,
  630. MessageDataType::Board,
  631. MessageActionType::Create,
  632. msg,
  633. )
  634. .await?;
  635. Ok(())
  636. }
  637. pub(crate) async fn post_task_create(
  638. conn: &mut PoolConnection<Postgres>,
  639. homeserver_url: String,
  640. matrix_user_id: String,
  641. organization_id: uuid::Uuid,
  642. token: String,
  643. task: &Task,
  644. ) -> Result<(), anyhow::Error> {
  645. let msg = format!(
  646. "New Task 🚀 \n {} day(s) Task: {}\n `{}`\n https://kinbrio.com/task/{}",
  647. task.estimated_quarter_days as f64 * 0.25,
  648. task.name,
  649. task.description,
  650. task.key
  651. );
  652. message(
  653. conn,
  654. matrix_user_id,
  655. organization_id,
  656. homeserver_url,
  657. token,
  658. MessageDataType::Task,
  659. MessageActionType::Create,
  660. msg,
  661. )
  662. .await?;
  663. Ok(())
  664. }