Move from XEP-0048 to Bookmarks 2 (This Time it’s Serious).
This commit is contained in:
parent
e9ce2838a6
commit
a3e6ef8c3a
4 changed files with 133 additions and 47 deletions
|
|
@ -54,9 +54,15 @@ fn main() {
|
||||||
Event::ContactChanged(contact) => {
|
Event::ContactChanged(contact) => {
|
||||||
println!("Contact {} changed.", contact.jid);
|
println!("Contact {} changed.", contact.jid);
|
||||||
},
|
},
|
||||||
Event::OpenRoomBookmark(bookmark) => {
|
Event::JoinRoom(jid, conference) => {
|
||||||
println!("Joining room “{}” ({})…", bookmark.name, bookmark.jid);
|
println!("Joining room {} ({:?})…", jid, conference.name);
|
||||||
agent.join_room(bookmark.jid, bookmark.nick, bookmark.password, "en", "Yet another bot!");
|
agent.join_room(jid, conference.nick, conference.password, "en", "Yet another bot!");
|
||||||
|
},
|
||||||
|
Event::LeaveRoom(jid) => {
|
||||||
|
println!("Leaving room {}…", jid);
|
||||||
|
},
|
||||||
|
Event::LeaveAllRooms => {
|
||||||
|
println!("Leaving all rooms…");
|
||||||
},
|
},
|
||||||
Event::RoomJoined(jid) => {
|
Event::RoomJoined(jid) => {
|
||||||
println!("Joined room {}.", jid);
|
println!("Joined room {}.", jid);
|
||||||
|
|
|
||||||
62
src/lib.rs
62
src/lib.rs
|
|
@ -17,11 +17,7 @@ use tokio_xmpp::{
|
||||||
Packet,
|
Packet,
|
||||||
};
|
};
|
||||||
use xmpp_parsers::{
|
use xmpp_parsers::{
|
||||||
bookmarks::{
|
bookmarks2::Conference,
|
||||||
Autojoin,
|
|
||||||
Conference as ConferenceBookmark,
|
|
||||||
Storage as Bookmarks,
|
|
||||||
},
|
|
||||||
caps::{compute_disco, hash_caps, Caps},
|
caps::{compute_disco, hash_caps, Caps},
|
||||||
disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity},
|
disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity},
|
||||||
hashes::Algo,
|
hashes::Algo,
|
||||||
|
|
@ -33,16 +29,13 @@ use xmpp_parsers::{
|
||||||
},
|
},
|
||||||
ns,
|
ns,
|
||||||
presence::{Presence, Type as PresenceType},
|
presence::{Presence, Type as PresenceType},
|
||||||
pubsub::{
|
pubsub::pubsub::{PubSub, Items},
|
||||||
event::PubSubEvent,
|
|
||||||
pubsub::PubSub,
|
|
||||||
},
|
|
||||||
roster::{Roster, Item as RosterItem},
|
roster::{Roster, Item as RosterItem},
|
||||||
stanza_error::{StanzaError, ErrorType, DefinedCondition},
|
stanza_error::{StanzaError, ErrorType, DefinedCondition},
|
||||||
Jid, BareJid, FullJid, JidParseError,
|
Jid, BareJid, FullJid, JidParseError,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod avatar;
|
mod pubsub;
|
||||||
|
|
||||||
pub type Error = tokio_xmpp::Error;
|
pub type Error = tokio_xmpp::Error;
|
||||||
|
|
||||||
|
|
@ -84,7 +77,9 @@ pub enum Event {
|
||||||
ContactRemoved(RosterItem),
|
ContactRemoved(RosterItem),
|
||||||
ContactChanged(RosterItem),
|
ContactChanged(RosterItem),
|
||||||
AvatarRetrieved(Jid, String),
|
AvatarRetrieved(Jid, String),
|
||||||
OpenRoomBookmark(ConferenceBookmark),
|
JoinRoom(BareJid, Conference),
|
||||||
|
LeaveRoom(BareJid),
|
||||||
|
LeaveAllRooms,
|
||||||
RoomJoined(BareJid),
|
RoomJoined(BareJid),
|
||||||
RoomLeft(BareJid),
|
RoomLeft(BareJid),
|
||||||
}
|
}
|
||||||
|
|
@ -141,7 +136,7 @@ impl ClientBuilder<'_> {
|
||||||
features.push(Feature::new(format!("{}+notify", ns::AVATAR_METADATA)));
|
features.push(Feature::new(format!("{}+notify", ns::AVATAR_METADATA)));
|
||||||
}
|
}
|
||||||
if self.features.contains(&ClientFeature::JoinRooms) {
|
if self.features.contains(&ClientFeature::JoinRooms) {
|
||||||
features.push(Feature::new(format!("{}+notify", ns::BOOKMARKS)));
|
features.push(Feature::new(format!("{}+notify", ns::BOOKMARKS2)));
|
||||||
}
|
}
|
||||||
DiscoInfoResult {
|
DiscoInfoResult {
|
||||||
node: None,
|
node: None,
|
||||||
|
|
@ -209,6 +204,10 @@ impl ClientBuilder<'_> {
|
||||||
let iq = Iq::from_get("roster", Roster { ver: None, items: vec![] })
|
let iq = Iq::from_get("roster", Roster { ver: None, items: vec![] })
|
||||||
.into();
|
.into();
|
||||||
sender_tx.unbounded_send(Packet::Stanza(iq)).unwrap();
|
sender_tx.unbounded_send(Packet::Stanza(iq)).unwrap();
|
||||||
|
// TODO: only send this when the JoinRooms feature is enabled.
|
||||||
|
let iq = Iq::from_get("bookmarks", PubSub::Items(Items::new(ns::BOOKMARKS2)))
|
||||||
|
.into();
|
||||||
|
sender_tx.unbounded_send(Packet::Stanza(iq)).unwrap();
|
||||||
}
|
}
|
||||||
TokioXmppEvent::Disconnected => {
|
TokioXmppEvent::Disconnected => {
|
||||||
events.push(Event::Disconnected);
|
events.push(Event::Disconnected);
|
||||||
|
|
@ -216,6 +215,8 @@ impl ClientBuilder<'_> {
|
||||||
TokioXmppEvent::Stanza(stanza) => {
|
TokioXmppEvent::Stanza(stanza) => {
|
||||||
if stanza.is("iq", "jabber:client") {
|
if stanza.is("iq", "jabber:client") {
|
||||||
let iq = Iq::try_from(stanza).unwrap();
|
let iq = Iq::try_from(stanza).unwrap();
|
||||||
|
let from =
|
||||||
|
iq.from.clone().unwrap_or_else(|| Jid::from_str(&jid).unwrap());
|
||||||
if let IqType::Get(payload) = iq.payload {
|
if let IqType::Get(payload) = iq.payload {
|
||||||
if payload.is("query", ns::DISCO_INFO) {
|
if payload.is("query", ns::DISCO_INFO) {
|
||||||
let query = DiscoInfoQuery::try_from(payload);
|
let query = DiscoInfoQuery::try_from(payload);
|
||||||
|
|
@ -245,15 +246,8 @@ impl ClientBuilder<'_> {
|
||||||
events.push(Event::ContactAdded(item));
|
events.push(Event::ContactAdded(item));
|
||||||
}
|
}
|
||||||
} else if payload.is("pubsub", ns::PUBSUB) {
|
} else if payload.is("pubsub", ns::PUBSUB) {
|
||||||
let pubsub = PubSub::try_from(payload).unwrap();
|
let new_events = pubsub::handle_iq_result(&from, payload);
|
||||||
let from =
|
events.extend(new_events);
|
||||||
iq.from.clone().unwrap_or_else(|| Jid::from_str(&jid).unwrap());
|
|
||||||
if let PubSub::Items(items) = pubsub {
|
|
||||||
if items.node.0 == ns::AVATAR_DATA {
|
|
||||||
let new_events = avatar::handle_data_pubsub_iq(&from, &items);
|
|
||||||
events.extend(new_events);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if let IqType::Set(_) = iq.payload {
|
} else if let IqType::Set(_) = iq.payload {
|
||||||
// We MUST answer unhandled set iqs with a service-unavailable error.
|
// We MUST answer unhandled set iqs with a service-unavailable error.
|
||||||
|
|
@ -264,28 +258,8 @@ impl ClientBuilder<'_> {
|
||||||
let from = message.from.clone().unwrap();
|
let from = message.from.clone().unwrap();
|
||||||
for child in message.payloads {
|
for child in message.payloads {
|
||||||
if child.is("event", ns::PUBSUB_EVENT) {
|
if child.is("event", ns::PUBSUB_EVENT) {
|
||||||
let event = PubSubEvent::try_from(child).unwrap();
|
let new_events = pubsub::handle_event(&from, child, &mut sender_tx);
|
||||||
if let PubSubEvent::PublishedItems { node, items } = event {
|
events.extend(new_events);
|
||||||
if node.0 == ns::AVATAR_METADATA {
|
|
||||||
let new_events = avatar::handle_metadata_pubsub_event(&from, &mut sender_tx, items);
|
|
||||||
events.extend(new_events);
|
|
||||||
} else if node.0 == ns::BOOKMARKS {
|
|
||||||
// TODO: Check that our bare JID is the sender.
|
|
||||||
assert_eq!(items.len(), 1);
|
|
||||||
let item = items.clone().pop().unwrap();
|
|
||||||
let payload = item.payload.clone().unwrap();
|
|
||||||
let bookmarks = match Bookmarks::try_from(payload) {
|
|
||||||
Ok(bookmarks) => bookmarks,
|
|
||||||
// XXX: Don’t panic…
|
|
||||||
Err(err) => panic!("… {}", err),
|
|
||||||
};
|
|
||||||
for bookmark in bookmarks.conferences {
|
|
||||||
if bookmark.autojoin == Autojoin::True {
|
|
||||||
events.push(Event::OpenRoomBookmark(bookmark));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if stanza.is("presence", "jabber:client") {
|
} else if stanza.is("presence", "jabber:client") {
|
||||||
|
|
@ -362,7 +336,7 @@ impl Agent {
|
||||||
let nick = nick.unwrap_or_else(|| self.default_nick.borrow().clone());
|
let nick = nick.unwrap_or_else(|| self.default_nick.borrow().clone());
|
||||||
let room_jid = room.with_resource(nick);
|
let room_jid = room.with_resource(nick);
|
||||||
let mut presence = Presence::new(PresenceType::None)
|
let mut presence = Presence::new(PresenceType::None)
|
||||||
.with_to(Some(Jid::Full(room_jid)));
|
.with_to(Jid::Full(room_jid));
|
||||||
presence.add_payload(muc);
|
presence.add_payload(muc);
|
||||||
presence.set_status(String::from(lang), String::from(status));
|
presence.set_status(String::from(lang), String::from(status));
|
||||||
let presence = presence.into();
|
let presence = presence.into();
|
||||||
|
|
|
||||||
106
src/pubsub/mod.rs
Normal file
106
src/pubsub/mod.rs
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
// Copyright (c) 2019 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
use crate::Event;
|
||||||
|
use futures::sync::mpsc;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use tokio_xmpp::Packet;
|
||||||
|
use xmpp_parsers::{
|
||||||
|
Jid, BareJid,
|
||||||
|
Element,
|
||||||
|
ns,
|
||||||
|
bookmarks2::{
|
||||||
|
Autojoin,
|
||||||
|
Conference,
|
||||||
|
},
|
||||||
|
pubsub::event::PubSubEvent,
|
||||||
|
pubsub::pubsub::PubSub,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) mod avatar;
|
||||||
|
|
||||||
|
pub(crate) fn handle_event(from: &Jid, elem: Element, mut tx: &mut mpsc::UnboundedSender<Packet>) -> impl IntoIterator<Item = Event> {
|
||||||
|
let mut events = Vec::new();
|
||||||
|
match PubSubEvent::try_from(elem) {
|
||||||
|
Ok(PubSubEvent::PublishedItems { node, items }) => {
|
||||||
|
match node.0 {
|
||||||
|
node if node == ns::AVATAR_METADATA => {
|
||||||
|
let new_events = avatar::handle_metadata_pubsub_event(&from, &mut tx, items);
|
||||||
|
events.extend(new_events);
|
||||||
|
},
|
||||||
|
node if node == ns::BOOKMARKS2 => {
|
||||||
|
// TODO: Check that our bare JID is the sender.
|
||||||
|
assert_eq!(items.len(), 1);
|
||||||
|
let item = items.clone().pop().unwrap();
|
||||||
|
let jid = BareJid::from_str(&item.id.clone().unwrap().0).unwrap();
|
||||||
|
let payload = item.payload.clone().unwrap();
|
||||||
|
match Conference::try_from(payload) {
|
||||||
|
Ok(conference) => {
|
||||||
|
if conference.autojoin == Autojoin::True {
|
||||||
|
events.push(Event::JoinRoom(jid, conference));
|
||||||
|
} else {
|
||||||
|
events.push(Event::LeaveRoom(jid));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => println!("not bookmark: {}", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
node => unimplemented!("node {}", node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(PubSubEvent::RetractedItems { node, items }) => {
|
||||||
|
match node.0 {
|
||||||
|
node if node == ns::BOOKMARKS2 => {
|
||||||
|
// TODO: Check that our bare JID is the sender.
|
||||||
|
assert_eq!(items.len(), 1);
|
||||||
|
let item = items.clone().pop().unwrap();
|
||||||
|
let jid = BareJid::from_str(&item.0).unwrap();
|
||||||
|
events.push(Event::LeaveRoom(jid));
|
||||||
|
},
|
||||||
|
node => unimplemented!("node {}", node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(PubSubEvent::Purge { node }) => {
|
||||||
|
match node.0 {
|
||||||
|
node if node == ns::BOOKMARKS2 => {
|
||||||
|
// TODO: Check that our bare JID is the sender.
|
||||||
|
events.push(Event::LeaveAllRooms);
|
||||||
|
},
|
||||||
|
node => unimplemented!("node {}", node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unimplemented!()
|
||||||
|
}
|
||||||
|
events
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn handle_iq_result(from: &Jid, elem: Element) -> impl IntoIterator<Item = Event> {
|
||||||
|
let mut events = Vec::new();
|
||||||
|
let pubsub = PubSub::try_from(elem).unwrap();
|
||||||
|
if let PubSub::Items(items) = pubsub {
|
||||||
|
if items.node.0 == ns::AVATAR_DATA {
|
||||||
|
let new_events = avatar::handle_data_pubsub_iq(&from, &items);
|
||||||
|
events.extend(new_events);
|
||||||
|
} else if items.node.0 == ns::BOOKMARKS2 {
|
||||||
|
events.push(Event::LeaveAllRooms);
|
||||||
|
for item in items.items {
|
||||||
|
let item = item.0;
|
||||||
|
let jid = BareJid::from_str(&item.id.clone().unwrap().0).unwrap();
|
||||||
|
let payload = item.payload.clone().unwrap();
|
||||||
|
match Conference::try_from(payload) {
|
||||||
|
Ok(conference) => {
|
||||||
|
if let Autojoin::True = conference.autojoin {
|
||||||
|
events.push(Event::JoinRoom(jid, conference));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => panic!("Wrong payload type in bookmarks 2 item: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
events
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue