use ruma::events::*;
pub use ruma::{
api::client::sync::sync_events::v3::Response, OwnedEventId, OwnedRoomAliasId, OwnedRoomId,
OwnedRoomName, OwnedServerName, OwnedUserId, RoomAliasId, RoomId, RoomName, ServerName, UserId,
};
use std::collections::BTreeMap;
use std::time::Duration;
const TIMEOUT: Duration = Duration::from_secs(3);
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct UserInfo {
pub user_id: OwnedUserId,
pub display_name_in_room: BTreeMap<OwnedRoomId, String>,
}
impl UserInfo {
pub fn new(user_id: OwnedUserId) -> Self {
Self {
user_id: user_id,
display_name_in_room: BTreeMap::new(),
}
}
pub fn get_name_string(&self, room_id: OwnedRoomId) -> String {
if self.display_name_in_room.contains_key(&room_id) {
self.display_name_in_room[&room_id].clone()
} else {
self.user_id.to_string()
}
}
pub fn set_display_name_in_room(&mut self, room_id: OwnedRoomId, name: impl Into<String>) {
self.display_name_in_room.insert(room_id, name.into());
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct StorageData {
pub auth_token: String,
pub room_to_aliases: BTreeMap<OwnedRoomId, Vec<OwnedRoomAliasId>>,
pub alias_to_room: BTreeMap<OwnedRoomAliasId, OwnedRoomId>,
pub joined_rooms: Vec<OwnedRoomId>,
pub room_id_to_cananoical_alias: BTreeMap<OwnedRoomId, OwnedRoomAliasId>,
pub room_to_name: BTreeMap<OwnedRoomId, OwnedRoomName>,
pub room_to_direct_name: BTreeMap<OwnedRoomId, String>,
pub recent_event_ids: Vec<OwnedEventId>,
pub last_sent_messages: Vec<(OwnedRoomId, String)>,
pub room_userid: BTreeMap<OwnedUserId, UserInfo>,
pub room_id_user_list: BTreeMap<OwnedRoomId, Vec<OwnedUserId>>,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct Client {
client: ruma::Client<ruma::client::http_client::Reqwest>,
pub me: OwnedUserId,
since: Option<String>,
host: String,
pub data: StorageData,
room_list_updated: bool,
}
pub enum Authentication {
Password(String),
AuthToken(String),
}
impl Client {
pub async fn new(
host: impl Into<String>,
username: impl Into<String>,
server_name: impl Into<String>,
authentication: Authentication,
) -> Self {
let good_host = Self::get_correct_host(host.into().trim_end_matches("/").to_string()).await;
let mut me = UserId::parse_with_server_name(
username.into(),
&ruma::ServerName::parse(server_name.into()).unwrap(),
)
.unwrap();
let client = match authentication {
Authentication::Password(x) => {
let c = ruma::Client::builder()
.homeserver_url(good_host.clone())
.build()
.await
.unwrap();
c.log_in(me.clone().as_str(), x.as_str(), None, None)
.await
.unwrap();
c
}
Authentication::AuthToken(x) => ruma::Client::builder()
.homeserver_url(good_host.clone())
.access_token(Some(x))
.build()
.await
.unwrap(),
};
me = client
.send_request(ruma::api::client::account::whoami::v3::Request::new())
.await
.unwrap()
.user_id;
Self {
client: client,
host: good_host, me: me,
since: None,
data: StorageData::default(),
room_list_updated: false,
}
}
pub fn room_list_updated(&mut self) -> bool {
if self.room_list_updated {
self.room_list_updated = false;
return true;
}
false
}
pub async fn get_correct_host(host: String) -> String {
let should_req_well_known = match host.split(":").last().unwrap().parse::<usize>() {
Ok(port) => {
if port != 443 {
false
} else {
true
}
}
Err(_) => true,
};
let mut correct_host = host.clone();
if should_req_well_known {
if let Ok(z) = ruma::client::http_client::Reqwest::new()
.get(&format!("{correct_host}/.well-known/matrix/client"))
.send()
.await
{
let resp: serde_json::Value =
serde_json::from_str(&z.text().await.unwrap()).unwrap();
let base_url: String = resp["m.homeserver"]["base_url"]
.as_str()
.unwrap()
.to_string();
correct_host = base_url.trim_end_matches("/").to_string();
}
}
correct_host
}
pub fn get_me(&self) -> OwnedUserId {
self.me.clone()
}
pub async fn join_room_id(
&self,
room_id: OwnedRoomId,
) -> ruma::api::client::membership::join_room_by_id::v3::Response {
self.client
.send_request(
ruma::api::client::membership::join_room_by_id::v3::Request::new(&room_id),
)
.await
.unwrap()
}
pub async fn sync(&mut self) -> Result<Response, ()> {
let mut req = ruma::api::client::sync::sync_events::v3::Request::new();
req.filter = None;
req.since = match self.since {
Some(ref x) => Some(x),
None => None,
};
req.full_state = self.since.clone().is_none();
req.timeout = Some(TIMEOUT);
if let Ok(res) = self.client.send_request(req).await {
self.since = Some(res.clone().next_batch);
return Ok(res);
} else {
return Err(());
}
}
pub async fn join_with_alias(&self, alias: impl Into<String>) {
let room_alias = alias.into();
self.join_room_id(self.room_id_from_alias(room_alias).await)
.await;
}
pub async fn leave_room_id(
&mut self,
room_id: OwnedRoomId,
) -> ruma::api::client::membership::leave_room::v3::Response {
let resp = self
.client
.send_request(ruma::api::client::membership::leave_room::v3::Request::new(
&room_id,
))
.await
.unwrap();
self.data.joined_rooms.retain(|x| x != &room_id);
self.room_list_updated = true;
resp
}
pub async fn room_id_from_alias(&self, alias: impl Into<String>) -> OwnedRoomId {
let room_alias_string = alias.into();
let room_alias = RoomAliasId::parse(room_alias_string.as_str()).unwrap();
self.client
.send_request(ruma::api::client::alias::get_alias::v3::Request::new(
&room_alias,
))
.await
.unwrap()
.room_id
}
pub async fn send_message(
&mut self,
room_id: OwnedRoomId,
message: impl Into<String>,
) -> ruma::api::client::message::send_message_event::v3::Response {
let msg = message.into();
let resp = self
.client
.send_request(
ruma::api::client::message::send_message_event::v3::Request::new(
&room_id,
&ruma::TransactionId::new(),
&ruma::events::room::message::RoomMessageEventContent::text_plain(msg.clone()),
)
.unwrap(),
)
.await
.unwrap();
self.data.last_sent_messages.push((room_id.clone(), msg));
resp
}
pub fn extract_data(&mut self, sync_results: Response) -> Vec<MessageInfo> {
let mut messages: Vec<MessageInfo> = Vec::new();
let x = sync_results.rooms.join;
for (room_id, joined_room) in x.iter() {
if !self.data.joined_rooms.contains(room_id) {
self.data.joined_rooms.push(room_id.clone());
self.data
.room_id_user_list
.insert(room_id.clone(), Vec::new());
self.room_list_updated = true;
}
let mut room_members: Vec<String> = Vec::new();
let mut is_direct = false;
for event in joined_room.state.events.clone() {
if let Ok(ev) = event.deserialize() {
if let ruma::events::AnySyncStateEvent::RoomAliases(SyncStateEvent::Original(
room_aliases_event,
)) = ev.clone()
{
let aliases = room_aliases_event.content.aliases;
for alias in aliases {
if !self.data.alias_to_room.contains_key(&alias) {
self.data
.alias_to_room
.insert(alias.clone(), room_id.clone());
}
if !self.data.room_to_aliases.contains_key(room_id) {
self.data
.room_to_aliases
.insert(room_id.clone(), vec![alias]);
} else {
if !self.data.room_to_aliases[room_id].contains(&alias) {
let mut current = self.data.room_to_aliases[room_id].clone();
current.push(alias.clone());
self.data.room_to_aliases.insert(room_id.clone(), current);
}
}
}
}
if let ruma::events::AnySyncStateEvent::RoomName(SyncStateEvent::Original(
room_name_event,
)) = ev.clone()
{
if let Some(room_name) = room_name_event.content.name {
self.data
.room_to_name
.insert(room_id.clone(), room_name.into());
}
} else if let ruma::events::AnySyncStateEvent::RoomCanonicalAlias(
SyncStateEvent::Original(room_canonical_alias_event),
) = ev.clone()
{
if let Some(alias) = room_canonical_alias_event.content.alias {
self.data
.room_id_to_cananoical_alias
.insert(room_id.clone(), alias);
}
} else if let ruma::events::AnySyncStateEvent::RoomMember(
SyncStateEvent::Original(room_member_event),
) = ev.clone()
{
self.room_list_updated = true;
let state_key_userid = UserId::parse(ev.state_key()).unwrap();
if !self.data.room_userid.contains_key(&state_key_userid) {
self.data.room_userid.insert(
state_key_userid.clone(),
UserInfo::new(state_key_userid.clone()),
);
}
if let Some(display_name) = room_member_event.content.displayname {
room_members.push(display_name.clone());
let mut user_info = self
.data
.room_userid
.get(&state_key_userid)
.unwrap()
.clone();
user_info.set_display_name_in_room(room_id.clone(), display_name);
self.data
.room_userid
.insert(state_key_userid.clone(), user_info);
}
if let Some(direct) = room_member_event.content.is_direct {
if !is_direct && direct == true {
room_members.pop();
is_direct = true;
}
}
if let ruma::events::room::member::MembershipState::Join =
room_member_event.content.membership
{
let mut user_list =
self.data.room_id_user_list.get(room_id).unwrap().clone();
user_list.push(state_key_userid.clone());
self.data
.room_id_user_list
.insert(room_id.clone(), user_list);
} else if let ruma::events::room::member::MembershipState::Leave =
room_member_event.content.membership
{
let mut user_list =
self.data.room_id_user_list.get(room_id).unwrap().clone();
user_list.retain(|x| x != &state_key_userid.clone());
self.data
.room_id_user_list
.insert(room_id.clone(), user_list);
} else if let ruma::events::room::member::MembershipState::Ban =
room_member_event.content.membership
{
let mut user_list =
self.data.room_id_user_list.get(room_id).unwrap().clone();
user_list.retain(|x| x != &state_key_userid.clone());
self.data
.room_id_user_list
.insert(room_id.clone(), user_list);
}
}
}
}
if is_direct {
if room_members.len() > 0 {
self.data
.room_to_direct_name
.insert(room_id.clone(), room_members[0].clone());
}
}
for message_info in joined_room.timeline.events.iter().filter_map(|m| {
if let Ok(ruma::events::AnySyncRoomEvent::MessageLike(message_event)) =
m.deserialize_as()
{
let msg_event_id = Box::new(message_event.event_id().to_owned());
if self.data.recent_event_ids.contains(&msg_event_id) {
return None;
}
if !self.data.recent_event_ids.contains(&msg_event_id) {
self.data.recent_event_ids.push(*msg_event_id);
}
if self.data.recent_event_ids.len() > 500 {
self.data.recent_event_ids.remove(0);
}
if let AnySyncMessageLikeEvent::RoomMessage(SyncMessageLikeEvent::Original(
room_message,
)) = message_event
{
let sender = room_message.sender;
let message: String;
let mut is_emote: bool = false;
if let room::message::MessageType::Text(text_message_event_content) =
room_message.content.msgtype
{
message = text_message_event_content.body;
} else if let room::message::MessageType::Emote(
emote_message_event_content,
) = room_message.content.msgtype
{
message = emote_message_event_content.body;
is_emote = true;
} else if let room::message::MessageType::Notice(
notice_message_event_content,
) = room_message.content.msgtype
{
message = notice_message_event_content.body;
} else {
return None;
}
if sender == self.me {
let look_for = &(room_id.clone(), message.clone());
if self.data.last_sent_messages.contains(&look_for) {
self.data.last_sent_messages.retain(|q| q != look_for);
return None;
}
}
return Some(MessageInfo {
message: message.clone(),
sender: sender,
room_id: room_id.clone(),
is_emote: is_emote,
});
}
}
None
}) {
messages.push(message_info);
}
}
messages
}
}
pub struct MessageInfo {
pub room_id: OwnedRoomId,
pub sender: OwnedUserId,
pub message: String,
pub is_emote: bool,
}