Skip to content

Commit

Permalink
Merge 'aa13q/master' (a bunch of improvements)
Browse files Browse the repository at this point in the history
Implement receive of messages, deleted history messages,
configurable server, typing status. PR#16

Closes #5
Closes #8
Closes #10
Closes #13
Closes #15
Closes #17
  • Loading branch information
Kaffeine committed Feb 26, 2019
2 parents 830936c + 9c9cfa3 commit 9869614
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 38 deletions.
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ find_package(TelepathyQt5Service 0.9.6 REQUIRED)

# Use the follow line to specify the path to QMatrixClient
# set(QMatrixClient_DIR "/usr/local/lib/cmake/QMatrixClient/")
find_package(QMatrixClient 0.4 REQUIRED)
find_package(QMatrixClient 0.5 REQUIRED)

find_package(Qt5 REQUIRED COMPONENTS Core Gui DBus Xml Network)
find_package(Qt5 REQUIRED COMPONENTS Core Gui DBus Xml Network Multimedia)

include(GNUInstallDirs)
include(CheckCXXCompilerFlag)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Tank is a Qt-based matrix connection operator for the Telepathy framework.
* CMake 3.2 (required by TelepathyQt)
* Qt 5.6
* [TelepathyQt 0.9.7](https://github.com/TelepathyIM/telepathy-qt)
* [libqmatrixclient 0.4](https://github.com/QMatrixClient/libqmatrixclient/)
* [libqmatrixclient 0.5](https://github.com/QMatrixClient/libqmatrixclient/)

Note: In order to use Tank, you need to have a complementary Telepathy Client application, such as KDE-Telepathy or Empathy.

Expand Down
63 changes: 43 additions & 20 deletions src/connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
#include <room.h>
#include <settings.h>
#include <user.h>
#include <events/roommemberevent.h>

#define Q_MATRIX_CLIENT_MAJOR_VERSION 0
#define Q_MATRIX_CLIENT_MINOR_VERSION 1
Expand Down Expand Up @@ -183,7 +182,7 @@ MatrixConnection::MatrixConnection(const QDBusConnection &dbusConnection, const
m_user = parameters.value(QLatin1String("user")).toString();
m_password = parameters.value(QLatin1String("password")).toString();
m_deviceId = parameters.value(QLatin1String("device"), QStringLiteral("HomePC")).toString();
m_server = parameters.value(QLatin1String("server")).toString();
m_server = parameters.value(QLatin1String("server"), QStringLiteral("https://matrix.org")).toString();

/* Connection.Interface.Avatars */
m_avatarsIface = Tp::BaseConnectionAvatarsInterface::create();
Expand All @@ -207,7 +206,7 @@ void MatrixConnection::doConnect(Tp::DBusError *error)
#if Q_MATRIX_CLIENT_VERSION >= Q_MATRIX_CLIENT_VERSION_CHECK(0, 2, 0)
m_connection = new QMatrixClient::Connection(this);
#else
m_connection = new QMatrixClient::Connection(QUrl(QStringLiteral("https://matrix.org")));
m_connection = new QMatrixClient::Connection(QUrl(m_server));
#endif
connect(m_connection, &QMatrixClient::Connection::connected, this, &MatrixConnection::onConnected);
connect(m_connection, &QMatrixClient::Connection::syncDone, this, &MatrixConnection::onSyncDone);
Expand All @@ -220,6 +219,7 @@ void MatrixConnection::doConnect(Tp::DBusError *error)
connect(m_connection, &QMatrixClient::Connection::resolveError, [](const QString &error) {
qDebug() << "Resolve error: " << error;
});
connect(m_connection, &QMatrixClient::Connection::newRoom, this, &MatrixConnection::processNewRoom);

if (loadSessionData()) {
qDebug() << Q_FUNC_INFO << "connectWithToken" << m_user << m_accessToken << m_deviceId;
Expand Down Expand Up @@ -457,6 +457,25 @@ uint MatrixConnection::setPresence(const QString &status, const QString &message
return selfHandle();
}

void MatrixConnection::onAboutToAddNewMessages(QMatrixClient::RoomEventsRange events)
{
for (auto &event : events) {
QMatrixClient::RoomMessageEvent *message = dynamic_cast<QMatrixClient::RoomMessageEvent *>(event.get());
if (message) {
QMatrixClient::Room *room = qobject_cast<QMatrixClient::Room *>(sender());
if (!room) {
continue;
}
MatrixMessagesChannelPtr textChannel = getMatrixMessagesChannelPtr(room);
if (!textChannel) {
qDebug() << Q_FUNC_INFO << "Error, channel is not a TextChannel?";
continue;
}
textChannel->processMessageEvent(message);
}
}
}

void MatrixConnection::onConnected()
{
m_userId = m_connection->userId();
Expand All @@ -470,26 +489,19 @@ void MatrixConnection::onConnected()
setStatus(Tp::ConnectionStatusConnected, Tp::ConnectionStatusReasonRequested);
m_contactListIface->setContactListState(Tp::ContactListStateWaiting);

m_connection->sync();

qDebug() << Q_FUNC_INFO;
saveSessionData();

m_connection->syncLoop();
}

void MatrixConnection::onSyncDone()
{
connect(m_connection, &QMatrixClient::Connection::newRoom, this, &MatrixConnection::processNewRoom);
qDebug() << Q_FUNC_INFO;
const auto rooms = m_connection->roomMap();
qDebug() << rooms;
// for (const QMatrixClient::Room *room : rooms) {
// qDebug() << room->toJson();
// }
for (QMatrixClient::Room *room : rooms) {
processNewRoom(room);
// connect(root, &QMatrixClient::Room::addedMessages)
}

m_contactListIface->setContactListState(Tp::ContactListStateSuccess);
}

Expand Down Expand Up @@ -550,7 +562,7 @@ bool MatrixConnection::saveSessionData() const
QJsonObject rootObject;
rootObject.insert("session", sessionObject);
rootObject.insert("format", c_sessionDataFormat);
QJsonDocument doc(sessionObject);
QJsonDocument doc(rootObject);

const QByteArray data = doc.toJson(QJsonDocument::Indented);

Expand Down Expand Up @@ -582,6 +594,9 @@ void MatrixConnection::processNewRoom(QMatrixClient::Room *room)
} else {
ensureHandle(room);
}
connect(room, &QMatrixClient::Room::aboutToAddNewMessages,
this, &MatrixConnection::onAboutToAddNewMessages,
Qt::UniqueConnection);
}

uint MatrixConnection::ensureDirectContact(QMatrixClient::User *user, QMatrixClient::Room *room)
Expand All @@ -592,18 +607,16 @@ uint MatrixConnection::ensureDirectContact(QMatrixClient::User *user, QMatrixCli
return handle;
}

void MatrixConnection::prefetchHistory(QMatrixClient::Room *room)
{
if (room->messageEvents().begin() == room->messageEvents().end()) {
return;
}

MatrixMessagesChannelPtr MatrixConnection::getMatrixMessagesChannelPtr(QMatrixClient::Room *room)
{
MatrixMessagesChannelPtr textChannel;
uint handleType = room->isDirectChat() ? Tp::HandleTypeContact : Tp::HandleTypeRoom;
uint handle = room->isDirectChat() ? getDirectContactHandle(room) : getRoomHandle(room);

if (!handle) {
qWarning() << Q_FUNC_INFO << "Unknown room" << room->id();
return;
return textChannel;
}

bool yoursChannel;
Expand All @@ -618,10 +631,20 @@ void MatrixConnection::prefetchHistory(QMatrixClient::Room *room)

if (error.isValid()) {
qWarning() << "ensureChannel failed:" << error.name() << " " << error.message();
return textChannel;
}

textChannel = MatrixMessagesChannelPtr::dynamicCast(channel->interface(TP_QT_IFACE_CHANNEL_TYPE_TEXT));
return textChannel;
}

void MatrixConnection::prefetchHistory(QMatrixClient::Room *room)
{
if (room->messageEvents().begin() == room->messageEvents().end()) {
return;
}

MatrixMessagesChannelPtr textChannel = MatrixMessagesChannelPtr::dynamicCast(channel->interface(TP_QT_IFACE_CHANNEL_TYPE_TEXT));
MatrixMessagesChannelPtr textChannel = getMatrixMessagesChannelPtr(room);

if (!textChannel) {
qDebug() << "Error, channel is not a TextChannel?";
Expand Down
6 changes: 6 additions & 0 deletions src/connection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

#include <QHash>

#include "messageschannel.hpp" // MatrixMessagesChannelPtr typedef

namespace QMatrixClient
{

Expand Down Expand Up @@ -110,6 +112,9 @@ class MatrixConnection : public Tp::BaseConnection

QMatrixClient::Connection *matrix() const { return m_connection; }

public slots:
void onAboutToAddNewMessages(QMatrixClient::RoomEventsRange events);

signals:
void messageReceived(const QString &sender, const QString &message);
void chatDetailsChanged(quint32 chatId, const Tp::UIntList &handles);
Expand Down Expand Up @@ -141,6 +146,7 @@ protected slots:
uint ensureHandle(QMatrixClient::Room *room);
uint ensureContactHandle(const QString &identifier);

MatrixMessagesChannelPtr getMatrixMessagesChannelPtr(QMatrixClient::Room *room);
void prefetchHistory(QMatrixClient::Room *room);

void startMechanismWithData_authCode(const QString &mechanism, const QByteArray &data, Tp::DBusError *error);
Expand Down
80 changes: 69 additions & 11 deletions src/messageschannel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
#include <connection.h>
#include <room.h>
#include <user.h>
#include <csapi/typing.h>
#include <events/typingevent.h>

MatrixMessagesChannel::MatrixMessagesChannel(MatrixConnection *connection, QMatrixClient::Room *room, Tp::BaseChannel *baseChannel)
: Tp::BaseChannelTextType(baseChannel),
Expand Down Expand Up @@ -59,11 +61,11 @@ MatrixMessagesChannel::MatrixMessagesChannel(MatrixConnection *connection, QMatr
deliveryReportingSupport);

baseChannel->plugInterface(Tp::AbstractChannelInterfacePtr::dynamicCast(m_messagesIface));
// m_messagesIface->setSendMessageCallback(Tp::memFun(this, &MatrixMessagesChannel::sendMessage));
m_messagesIface->setSendMessageCallback(Tp::memFun(this, &MatrixMessagesChannel::sendMessage));

// m_chatStateIface = Tp::BaseChannelChatStateInterface::create();
// m_chatStateIface->setSetChatStateCallback(Tp::memFun(this, &MatrixMessagesChannel::setChatState));
// baseChannel->plugInterface(Tp::AbstractChannelInterfacePtr::dynamicCast(m_chatStateIface));
m_chatStateIface = Tp::BaseChannelChatStateInterface::create();
m_chatStateIface->setSetChatStateCallback(Tp::memFun(this, &MatrixMessagesChannel::setChatState));
baseChannel->plugInterface(Tp::AbstractChannelInterfacePtr::dynamicCast(m_chatStateIface));

if (m_targetHandleType == Tp::HandleTypeRoom) {
Tp::ChannelGroupFlags groupFlags = 0;
Expand All @@ -90,6 +92,7 @@ MatrixMessagesChannel::MatrixMessagesChannel(MatrixConnection *connection, QMatr
}

connect(m_room, &QMatrixClient::Room::pendingEventChanged, this, &MatrixMessagesChannel::onPendingEventChanged);
connect(m_room, &QMatrixClient::Room::typingChanged, this, &MatrixMessagesChannel::onTypingChanged);
}

void MatrixMessagesChannel::onPendingEventChanged(int pendingEventIndex)
Expand Down Expand Up @@ -119,18 +122,26 @@ void MatrixMessagesChannel::onPendingEventChanged(int pendingEventIndex)
header[QStringLiteral("message-sender-id")] = QDBusVariant(m_targetId);
header[QStringLiteral("message-type")] = QDBusVariant(Tp::ChannelTextMessageTypeDeliveryReport);
header[QStringLiteral("delivery-status")] = QDBusVariant(tpDeliveryStatus);
header[QStringLiteral("delivery-token")] = QDBusVariant(pendingEvent.event()->transactionId());
header[QStringLiteral("delivery-token")] = QDBusVariant(pendingEvent.event()->id());
partList << header;

addReceivedMessage(partList);
}

void MatrixMessagesChannel::sendChatStateNotification(uint state)
{
m_room->connection()->
callApi<QMatrixClient::SetTypingJob>
(QMatrixClient::BackgroundRequest,
m_connection->matrix()->user()->id(), m_room->id(), (state == Tp::ChannelChatStateComposing));
}

MatrixMessagesChannelPtr MatrixMessagesChannel::create(MatrixConnection *connection, QMatrixClient::Room *room, Tp::BaseChannel *baseChannel)
{
return MatrixMessagesChannelPtr(new MatrixMessagesChannel(connection, room, baseChannel));
}

QString MatrixMessagesChannel::sendMessageCallback(const Tp::MessagePartList &messageParts, uint flags, Tp::DBusError *error)
QString MatrixMessagesChannel::sendMessage(const Tp::MessagePartList &messageParts, uint flags, Tp::DBusError *error)
{
QString content;
for (const Tp::MessagePart &part : messageParts) {
Expand All @@ -152,26 +163,31 @@ void MatrixMessagesChannel::processMessageEvent(const QMatrixClient::RoomMessage
qDebug().noquote() << Q_FUNC_INFO << "Process message" << doc.toJson(QJsonDocument::Indented);
Tp::MessagePart header;
header[QStringLiteral("message-token")] = QDBusVariant(event->id());
header[QStringLiteral("message-sent")] = QDBusVariant(event->timestamp().toMSecsSinceEpoch() / 1000);
header[QStringLiteral("message-sent")] = QDBusVariant(event->timestamp().toMSecsSinceEpoch() / 1000);
header[QStringLiteral("message-received")] = QDBusVariant(event->timestamp().toMSecsSinceEpoch() / 1000);
header[QStringLiteral("message-type")] = QDBusVariant(Tp::ChannelTextMessageTypeNormal);
if (event->senderId() == m_connection->matrix()->user()->id()) {
header[QStringLiteral("message-sender")] = QDBusVariant(m_connection->selfHandle());
header[QStringLiteral("message-sender")] = QDBusVariant(m_connection->selfHandle());
header[QStringLiteral("message-sender-id")] = QDBusVariant(m_connection->selfID());
} else {
header[QStringLiteral("message-sender")] = QDBusVariant(m_connection->ensureContactHandle(event->senderId()));
header[QStringLiteral("message-sender")] = QDBusVariant(m_connection->ensureContactHandle(event->senderId()));
header[QStringLiteral("message-sender-id")] = QDBusVariant(event->senderId());
}

/* Redacted deleted message */
// https://matrix.org/docs/spec/client_server/r0.4.0.html#id259
if (event->isRedacted())
header[QStringLiteral("delivery-status")] = QDBusVariant(Tp::DeliveryStatusDeleted);

/* Text message */
Tp::MessagePartList body;
Tp::MessagePart text;

text[QStringLiteral("content-type")] = QDBusVariant(QStringLiteral("text/plain"));
text[QStringLiteral("content")] = QDBusVariant(event->plainBody());
text[QStringLiteral("content")] = QDBusVariant(event->isRedacted() ? event->redactionReason() : event->plainBody());
body << text;

Tp::MessagePartList partList;
header[QStringLiteral("message-type")] = QDBusVariant(Tp::ChannelTextMessageTypeNormal);
partList << header << body;
addReceivedMessage(partList);
}
Expand All @@ -185,3 +201,45 @@ void MatrixMessagesChannel::fetchHistory()
}
}
}

void MatrixMessagesChannel::onTypingChanged()
{
if (m_room->usersTyping().isEmpty())
{
for(auto user: m_room->users())
{
const uint handle = m_connection->ensureContactHandle(user->id());
m_chatStateIface->chatStateChanged(handle, Tp::ChannelChatStateActive);
}
return;
}
for(auto user: m_room->usersTyping())
{
const uint handle = m_connection->ensureContactHandle(user->id());
m_chatStateIface->chatStateChanged(handle, Tp::ChannelChatStateComposing);
}
}

void MatrixMessagesChannel::reactivateLocalTyping()
{
sendChatStateNotification(Tp::ChannelChatStateComposing);
}

void MatrixMessagesChannel::setChatState(uint state, Tp::DBusError *error)
{
Q_UNUSED(error);

if (!m_localTypingTimer) {
m_localTypingTimer = new QTimer(this);
constexpr int c_chatStateResendInterval = 5000;
m_localTypingTimer->setInterval(c_chatStateResendInterval);
connect(m_localTypingTimer, &QTimer::timeout, this, &MatrixMessagesChannel::reactivateLocalTyping);
}

if (state == Tp::ChannelChatStateComposing) {
m_localTypingTimer->start();
} else {
m_localTypingTimer->stop();
}
sendChatStateNotification(state);
}
14 changes: 10 additions & 4 deletions src/messageschannel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <QPointer>

#include <TelepathyQt/BaseChannel>
#include <events/roommessageevent.h>

class QTimer;

Expand Down Expand Up @@ -61,17 +62,20 @@ class MatrixMessagesChannel : public Tp::BaseChannelTextType
public:
static MatrixMessagesChannelPtr create(MatrixConnection *connection, QMatrixClient::Room *room, Tp::BaseChannel *baseChannel);

QString sendMessageCallback(const Tp::MessagePartList &messageParts, uint flags, Tp::DBusError *error);
void processMessageEvent(const QMatrixClient::RoomMessageEvent *event);

void messageAcknowledgedCallback(const QString &messageId);
QString sendMessage(const Tp::MessagePartList &messageParts, uint flags, Tp::DBusError *error);
// void messageAcknowledged(const QString &messageId);
void setChatState(uint state, Tp::DBusError *error);

void fetchHistory();
void processMessageEvent(const QMatrixClient::RoomMessageEvent *event);

private:
MatrixMessagesChannel(MatrixConnection *connection, QMatrixClient::Room *room, Tp::BaseChannel *baseChannel);

void onPendingEventChanged(int pendingEventIndex);
void onTypingChanged();
void reactivateLocalTyping();
void sendChatStateNotification(uint state);

MatrixConnection *m_connection = nullptr;
QMatrixClient::Room *m_room = nullptr;
Expand All @@ -87,6 +91,8 @@ class MatrixMessagesChannel : public Tp::BaseChannelTextType
Tp::BaseChannelGroupInterfacePtr m_groupIface;
Tp::BaseChannelRoomInterfacePtr m_roomIface;
Tp::BaseChannelRoomConfigInterfacePtr m_roomConfigIface;

QTimer *m_localTypingTimer = nullptr;
};

#endif // TANK_MESSAGES_CHANNEL_HPP

0 comments on commit 9869614

Please sign in to comment.