diff --git a/.gitignore b/.gitignore
index ec8c70c..37192ac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,4 @@
+.project
*.user
-
-/.project
-
-*.autosave
\ No newline at end of file
+*.autosave
+*.o
diff --git a/doc/ru/main.md b/doc/ru/main.md
index e2c2c33..545c467 100644
--- a/doc/ru/main.md
+++ b/doc/ru/main.md
@@ -1,22 +1,13 @@
# Общая документация мессенджера Influence
-## Структура мессенджера
-
-Связь между узлами - точка-точка
-
-GUI - API ==== API - GUI
-
-GUI - примитивная программа под Linux/Windows/Android, которая умеет отображать сообщения и вызывать функции ядра (API).
-API - ядро мессенджера. Достаточно подключить *main.hpp*, чтобы вызывать функции ядра. Такая структура позволяет писать какие угодно клиенты.
-
## Протокол обмена
Обмен сообщениями ведётся по UDP протоколу в формате JSON.
-При любой успешной отправке данных обновляем поле last_connect у контакта.
-Если пришло сообщение от контакта, которого нет в адресной книге - то оно отбрасывается.
+При любой успешной отправке данных обновляем поле last_connect у контакта. (not implemented)
+Если пришло сообщение от контакта, которого нет в адресной книге - то оно отбрасывается.
Исключения - запрос handshake.
-### Добавление в друзья
+### Добавление в друзья (not implemented)
Используется, чтобы установить связь с контактом. Тот кто хочет добавиться в друзья - отправляет такой запрос:
@@ -32,8 +23,7 @@ API - ядро мессенджера. Достаточно подключить
```json
{
"action":"handshakeSuccess",
- "peerID":"*IPv6*",
- "status": true
+ "peerID":"*IPv6*"
}
```
@@ -72,7 +62,7 @@ API - ядро мессенджера. Достаточно подключить
Если нет ответа, то сохраняем сообщение у себя в истории, чтобы потом переотправить.
`status == true` - доставлено.
-### Пинг - проверка онлайна
+### Пинг - проверка онлайна (not implemented)
Если прошло время last_connect > 1 минуты, от отправляем запрос
@@ -113,7 +103,7 @@ API - ядро мессенджера. Достаточно подключить
В ответ приходит массив с `true` или `false`. Если `true` - сообщение успешно отправлено, `false` - сообщение недоставлено.
-### Отправление сообщения для другого абонента (это нужно чтобы отправлять тем, кто в offline)
+### Отправление сообщения для другого абонента (это нужно чтобы отправлять тем, кто в offline) (not implemented)
Если человек, которому мы отправляем сообщение offline - то, во-первых, оно сохраняется у себя на сервере, для последующей отправки.
Во-вторых, можно попросить какого-то друга его переотправить. Для этого выбираем случайно несколько друзей из списка контактов, у которых `can_resend == 1` и отправляем им запрос.
diff --git a/src/Influence.pro b/src/Influence.pro
index 97a68fb..ad05a91 100644
--- a/src/Influence.pro
+++ b/src/Influence.pro
@@ -27,12 +27,15 @@ SOURCES += \
main.cpp \
mainwindow.cpp \
kernel/network.cpp \
- kernel/handler.cpp
+ kernel/handler.cpp \
+ chatwindow.cpp
HEADERS += \
kernel/network.hpp \
mainwindow.h \
- kernel/handler.hpp
+ kernel/handler.hpp \
+ chatwindow.hpp
FORMS += \
- mainwindow.ui
+ mainwindow.ui \
+ chatwindow.ui
diff --git a/src/chatwindow.cpp b/src/chatwindow.cpp
new file mode 100644
index 0000000..b9dc5ef
--- /dev/null
+++ b/src/chatwindow.cpp
@@ -0,0 +1,70 @@
+#include "chatwindow.hpp"
+#include "ui_chatwindow.h"
+
+ChatWindow::ChatWindow(QString pID, QString cUUID, Handler *h, Network *n, QWidget *parent) :
+ QWidget(parent),
+ ui(new Ui::ChatWindow)
+{
+ ui->setupUi(this);
+ this->setAttribute(Qt::WA_DeleteOnClose);
+ chatID = cUUID;
+ peerID = pID;
+ handler = h;
+ network = n;
+ ui->peerIDLabel->setText(ui->peerIDLabel->text() + pID);
+ ui->chatIDLabel->setText(ui->chatIDLabel->text() + cUUID);
+ connect(ui->sendMsgButton, &QAbstractButton::clicked, this, &ChatWindow::sendMsgButtonClicked);
+}
+ChatWindow::~ChatWindow()
+{
+ leftFromChat();
+ emit deleteChat(chatID);
+ delete ui;
+ this->deleteLater();
+}
+
+void ChatWindow::sendMessage(QString msgText)
+{
+ QJsonObject jSend;
+ jSend["action"] = "msgSend";
+ jSend["peerID"] = *network->myIPv6;
+ jSend["chatID"] = chatID;
+ jSend["msgText"] = msgText;
+ network->sendDatagram(jSend, peerID);
+ QDateTime date(QDateTime::currentDateTime());
+ ui->chatEdit->append("[" + date.toString() + "] " + tr("You: ") + msgText);
+}
+
+void ChatWindow::sendMsgButtonClicked()
+{
+ QString msg = ui->msgEdit->text();
+ sendMessage(msg);
+ ui->msgEdit->setText("");
+}
+
+void ChatWindow::displayMsg(QString msgText)
+{
+ QDateTime date(QDateTime::currentDateTime());
+ ui->chatEdit->append("[" + date.toString() + "] " + "" + peerID + "" + ": " + msgText);
+}
+
+void ChatWindow::leftFromChat()
+{
+ QJsonObject json;
+ json["action"] = "leftChat";
+ json["peerID"] = *network->myIPv6;
+ json["chatID"] = chatID;
+ network->sendDatagram(json, peerID);
+}
+
+void ChatWindow::peerReceiverLeftFromChat()
+{
+ QString msg;
+ msg += tr("Peer ");
+ msg += peerID;
+ msg += tr(" left from this chat.");
+ QMessageBox::warning(this, tr("Peer receiver left from chat!"), msg, QMessageBox::Ok);
+ ui->chatEdit->append("" + peerID + "" + tr(" left from this chat."));
+ ui->msgEdit->setEnabled(false);
+ ui->sendMsgButton->setEnabled(false);
+}
diff --git a/src/chatwindow.hpp b/src/chatwindow.hpp
new file mode 100644
index 0000000..1da87cd
--- /dev/null
+++ b/src/chatwindow.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include
+#include
+#include "kernel/handler.hpp"
+#include "kernel/network.hpp"
+
+namespace Ui {
+ class ChatWindow;
+}
+
+class ChatWindow : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ explicit ChatWindow(QString pID, QString cUUID, Handler *h, Network *n, QWidget *parent = 0);
+ ~ChatWindow();
+ QString chatID;
+ QString peerID;
+ Handler *handler;
+ Network *network;
+ void displayMsg(QString msgText);
+ void peerReceiverLeftFromChat();
+ private:
+ Ui::ChatWindow *ui;
+ void sendMessage(QString msgText);
+ void leftFromChat();
+ signals:
+ void deleteChat(QString cID);
+ private slots:
+ void sendMsgButtonClicked();
+};
diff --git a/src/chatwindow.ui b/src/chatwindow.ui
new file mode 100644
index 0000000..76458d5
--- /dev/null
+++ b/src/chatwindow.ui
@@ -0,0 +1,78 @@
+
+
+ ChatWindow
+
+
+
+ 0
+ 0
+ 807
+ 519
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+ 697
+ 0
+
+
+
+ Contact PeerID:
+
+
+
+ -
+
+
+
+
+
+ Send
+
+
+
+ -
+
+
+ true
+
+
+
+ -
+
+
+ ChatID:
+
+
+
+
+
+
+
+
+ msgEdit
+ returnPressed()
+ sendMsgButton
+ click()
+
+
+ 360
+ 497
+
+
+ 757
+ 497
+
+
+
+
+
diff --git a/src/kernel/handler.cpp b/src/kernel/handler.cpp
index 50f75b9..14646dd 100644
--- a/src/kernel/handler.cpp
+++ b/src/kernel/handler.cpp
@@ -2,24 +2,101 @@
Handler::Handler()
{
+ using namespace std::placeholders;
+
+ handlers = {
+ {"checkPeer", std::bind(&Handler::checkPeer, this, _1)},
+ {"checkPeerSuccess", std::bind(&Handler::checkPeerSuccessMethod, this, _1)},
+ {"createChat", std::bind(&Handler::createChatMethod, this, _1)},
+ {"createChatSuccess", std::bind(&Handler::createChatSuccessMethod, this, _1)},
+ {"createChatFailed", std::bind(&Handler::createChatFailedMethod, this, _1)},
+ {"msgSend", std::bind(&Handler::msgReceiveMethod, this, _1)},
+ {"leftChat", std::bind(&Handler::peerReceiverLeftFromChatMethod, this, _1)}
+ };
+ peerReceiver = new QString();
network = new Network();
- connect(network, &Network::json_received, this, &Handler::handle);
+ connect(network, &Network::jsonReceived, this, &Handler::handle);
}
void Handler::handle(QJsonObject jsonReceived)
+{
+ QString action = jsonReceived["action"].toString();
+ *peerReceiver = jsonReceived["peerID"].toString();
+ handlers[action](jsonReceived);
+}
+
+void Handler::checkPeer(QJsonObject jsonReceived)
{
QJsonObject jsonSend;
- if(jsonReceived["action"] == "createSession" && !jsonReceived.contains("status"))
+ jsonSend["peerID"] = *network->myIPv6;
+ jsonSend["action"] = "checkPeerSuccess";
+ network->sendDatagram(jsonSend, *peerReceiver);
+}
+
+void Handler::checkPeerSuccessMethod(QJsonObject jsonReceived)
+{
+ emit checkPeerSuccess();
+}
+
+Handler::~Handler()
+{
+ delete peerReceiver;
+ delete network;
+ //delete handlers;
+}
+
+void Handler::createChatMethod(QJsonObject jsonReceived)
+{
+ QWidget *parent = 0;
+ QString msgTitle;
+ QString msg;
+ msgTitle += "Create chat";
+ msg += tr("Peer ");
+ msg += *peerReceiver;
+ msg += tr(" want to create chat with you.\n");
+ msg += tr("Do you want to create chat?");
+ int ret = QMessageBox::warning(parent, msgTitle, msg, QMessageBox::Yes | QMessageBox::No);
+ if(ret == QMessageBox::Yes)
{
- jsonSend["peerID"] = my_ipv6;
- jsonSend["action"] = "createSession";
- jsonSend["status"] = true;
- QString peerReceiver = jsonReceived["peerID"].toString();
- network->sendDatagram(jsonSend, peerReceiver);
+ QJsonObject jsonSend;
+ jsonSend["peerID"] = *network->myIPv6;
+ jsonSend["action"] = "createChatSuccess";
+ jsonSend["chatID"] = jsonReceived["chatUUID"].toString();
+ network->sendDatagram(jsonSend, *peerReceiver);
+ emit createChatSuccess(jsonReceived["peerID"].toString(), jsonReceived["chatUUID"].toString());
}
else
{
- emit createSessionSuccess();
+ QJsonObject jsonSend;
+ jsonSend["peerID"] = *network->myIPv6;
+ jsonSend["action"] = "createChatFailed";
+ network->sendDatagram(jsonSend, *peerReceiver);
}
}
+
+void Handler::createChatFailedMethod(QJsonObject jsonReceived)
+{
+ QString msg;
+ msg += tr("Peer ");
+ msg += *peerReceiver;
+ msg += tr(" refused to create a chat with you");
+ QWidget *parent = 0;
+ QMessageBox::critical(parent, tr("Create chat failed!"), msg, QMessageBox::Ok);
+ emit createChatFailed();
+}
+
+void Handler::createChatSuccessMethod(QJsonObject jsonReceived)
+{
+ emit createChatSuccess(jsonReceived["peerID"].toString(), jsonReceived["chatID"].toString());
+}
+
+void Handler::msgReceiveMethod(QJsonObject jsonReceived)
+{
+ emit msgReceived(jsonReceived["peerID"].toString(), jsonReceived["chatID"].toString(), jsonReceived["msgText"].toString());
+}
+
+void Handler::peerReceiverLeftFromChatMethod(QJsonObject jsonReceived)
+{
+ emit peerReceiverLeftFromChat(jsonReceived["peerID"].toString(), jsonReceived["chatID"].toString());
+}
diff --git a/src/kernel/handler.hpp b/src/kernel/handler.hpp
index e2ace3a..de2dca9 100644
--- a/src/kernel/handler.hpp
+++ b/src/kernel/handler.hpp
@@ -2,21 +2,32 @@
#include "network.hpp"
#include
+#include
class Handler : public QObject
{
Q_OBJECT
- const QString my_ipv6 = Network::local_ipv6();
-
public:
Handler();
- signals:
- void createSessionSuccess();
+ ~Handler();
+ QString *peerReceiver;
private:
Network *network;
+ void checkPeer(QJsonObject jsonReceived);
+ std::map> handlers;
+ void checkPeerSuccessMethod(QJsonObject jsonReceived);
+ void createChatMethod(QJsonObject jsonReceived);
+ void createChatSuccessMethod(QJsonObject jsonReceived);
+ void createChatFailedMethod(QJsonObject jsonReceived);
+ void msgReceiveMethod(QJsonObject jsonReceived);
+ void peerReceiverLeftFromChatMethod(QJsonObject jsonReceived);
+ signals:
+ void checkPeerSuccess();
+ void createChatSuccess(QString peerID, QString chatID);
+ void createChatFailed();
+ void msgReceived(QString peerID, QString chatID, QString msgText);
+ void peerReceiverLeftFromChat(QString peerID, QString chatID);
private slots:
void handle(QJsonObject jsonReceived);
-
-
};
diff --git a/src/kernel/network.cpp b/src/kernel/network.cpp
index f90cf17..c055a6a 100644
--- a/src/kernel/network.cpp
+++ b/src/kernel/network.cpp
@@ -1,4 +1,5 @@
#include "network.hpp"
+
Network::Network(bool is_server)
{
udpSocket = new QUdpSocket(this);
@@ -7,6 +8,7 @@ Network::Network(bool is_server)
udpSocket->bind(QHostAddress::AnyIPv6, 6552);
connect(udpSocket, SIGNAL(readyRead()), this, SLOT(processTheDatagram()));
}
+ myIPv6 = new QString(localIPv6());
}
void Network::sendDatagram(QJsonObject j, QString s)
@@ -23,21 +25,35 @@ void Network::processTheDatagram()
{
QByteArray baDatagram;
do {
- baDatagram.resize(udpSocket->pendingDatagramSize ()) ;
+ baDatagram.resize(udpSocket->pendingDatagramSize()) ;
udpSocket->readDatagram (baDatagram.data(), baDatagram.size()) ;
} while (udpSocket->hasPendingDatagrams()) ;
QJsonDocument jbuff = QJsonDocument::fromJson(baDatagram);
QJsonObject j = QJsonObject(jbuff.object());
- emit json_received(j);
+ emit jsonReceived(j);
}
-QString Network::local_ipv6()
+QString Network::localIPv6()
{
QHostAddress address;
+ QString addressString;
foreach (address, QNetworkInterface::allAddresses()) {
- if (address.protocol() == QAbstractSocket::IPv6Protocol && address != QHostAddress(QHostAddress::LocalHost))
- break;
+ if (address.protocol() == QAbstractSocket::IPv6Protocol && address != QHostAddress(QHostAddress::LocalHostIPv6) && (address.toString()).contains("fc"))
+ {
+ addressString = address.toString();
+ break;
+ }
+ else
+ {
+ addressString = "null";
+ }
}
- return(address.toString());
+ return(addressString);
+}
+
+Network::~Network()
+{
+ delete udpSocket;
+ delete myIPv6;
}
diff --git a/src/kernel/network.hpp b/src/kernel/network.hpp
index 6b0679f..f94b89f 100644
--- a/src/kernel/network.hpp
+++ b/src/kernel/network.hpp
@@ -9,15 +9,18 @@ class Network : public QObject
{
Q_OBJECT
- private:
- QUdpSocket* udpSocket;
+
public:
Network(bool is_server = true);
- static QString local_ipv6();
+ ~Network();
+ static QString localIPv6();
+ const QString *myIPv6;
+ private:
+ QUdpSocket* udpSocket;
public slots:
void sendDatagram(QJsonObject j, QString s);
signals:
- void json_received(QJsonObject &jsonReceived);
+ void jsonReceived(QJsonObject &jsonReceived);
private slots:
void processTheDatagram();
};
diff --git a/src/main.cpp b/src/main.cpp
index b48f94e..c86e878 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -5,6 +5,10 @@ int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
+ if(w.doQuit())
+ {
+ return -1;
+ }
w.show();
return a.exec();
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 49df6fa..ddb1da2 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -5,47 +5,140 @@ MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
- ui->setupUi(this);
network = new Network(false);
+ if(*network->myIPv6 == "null")
+ {
+ QMessageBox::critical(parent, tr("Cjdns is not running!"), tr("Cjdns is not running, so the application will be closed."));
+ mDoQuit = true;
+ }
+ ui->setupUi(this);
handler = new Handler();
- connect(handler, &Handler::createSessionSuccess, this, &MainWindow::peerReceiverConnected);
+ timer = new QTimer();
+ connect(timer, SIGNAL(timeout()), this, SLOT(slotTimerAlarm()));
+ connect(handler, &Handler::checkPeerSuccess, this, &MainWindow::peerReceiverAvailable);
+ connect(handler, &Handler::createChatSuccess, this, &MainWindow::createChat);
+ connect(handler, &Handler::createChatFailed, this, &MainWindow::createChatFailedMethod);
+ connect(handler, &Handler::msgReceived, this, &MainWindow::msgReceivedMethod);
+ connect(handler, &Handler::peerReceiverLeftFromChat, this, &MainWindow::peerReceiverLeftFromChatMethod);
+ ui->myIP->setText(*network->myIPv6);
+
}
MainWindow::~MainWindow()
{
delete ui;
+ delete network;
+ delete handler;
+ delete timer;
}
void MainWindow::on_connectToPeer_clicked()
{
+ setButtonToWaiting();
QJsonObject j;
- j["peerID"] = my_ipv6;
- j["action"] = "createSession";
+ j["peerID"] = *network->myIPv6;
+ j["action"] = "checkPeer";
QString s = ui->peerID->text();
network->sendDatagram(j, s);
- timer = new QTimer();
- connect(timer, SIGNAL(timeout()), this, SLOT(slotTimerAlarm()));
timer->start(10000);
}
void MainWindow::slotTimerAlarm()
{
- if(receive)
+ if(receive == true)
{
timer->stop();
receive = false;
}
else
{
- int ret = QMessageBox::critical(this,QObject::tr("Error"),tr("Timeout Error"));
+ QMessageBox::critical(this, tr("Error"), tr("Timeout Error\n\nPerhaps you have not started the cjdns daemon, you entered the wrong IP, the peer is off, or the peer did not start the messenger."));
timer->stop();
- delete timer;
+ setButtonToConnect();
}
}
-void MainWindow::peerReceiverConnected()
+void MainWindow::peerReceiverAvailable()
{
receive = true;
- int ret = QMessageBox::information(this,QObject::tr("Info"),tr("Peer Available!"));
+ int ret = QMessageBox::information(this, tr("Peer Available!"),
+ tr("Peer Available!\nDo you want to create chat?"), QMessageBox::Yes | QMessageBox::No);
+ if(ret == QMessageBox::Yes)
+ {
+ createChatSendDatagram(*handler->peerReceiver);
+ }
+ else
+ {
+ setButtonToConnect();
+ }
+}
+
+void MainWindow::setButtonToWaiting() // Function, which sets button "connectToPeer" in status "Waiting..."
+{
+ ui->connectToPeer->setEnabled(false);
+ ui->connectToPeer->setText(tr("Waiting..."));
+}
+
+void MainWindow::setButtonToConnect() // Function, which sets button "connectToPeer" in status "Connect"
+{
+ ui->connectToPeer->setEnabled(true);
+ ui->connectToPeer->setText(tr("Connect"));
+}
+
+void MainWindow::createChatSendDatagram(QString peerReceiver)
+{
+ QUuid chatID = QUuid::createUuid();
+ QJsonObject jSend;
+ jSend["action"] = "createChat";
+ jSend["peerID"] = *network->myIPv6;
+ jSend["chatUUID"] = chatID.toString();
+ network->sendDatagram(jSend, peerReceiver);
+}
+
+void MainWindow::createChat(QString peerID, QString chatID)
+{
+ pChatWindows.push_back(new ChatWindow(peerID, chatID, handler, network));
+ connect(pChatWindows.back(), &ChatWindow::deleteChat, this, &MainWindow::deleteChatMethod);
+ pChatWindows.back()->show();
+ setButtonToConnect();
+}
+
+void MainWindow::createChatFailedMethod()
+{
+ setButtonToConnect();
+}
+
+void MainWindow::deleteChatMethod(QString cID)
+{
+ for(int i = 0; i < pChatWindows.size(); i++)
+ {
+ if(pChatWindows.at(i)->chatID == cID)
+ {
+ pChatWindows.remove(i);
+ break;
+ }
+ }
+}
+
+void MainWindow::msgReceivedMethod(QString peerID, QString chatID, QString msgText)
+{
+ for(int i = 0; i < pChatWindows.size(); i++)
+ {
+ if(pChatWindows.at(i)->peerID == peerID && pChatWindows.at(i)->chatID == chatID)
+ {
+ pChatWindows.at(i)->displayMsg(msgText);
+ }
+ }
+}
+
+void MainWindow::peerReceiverLeftFromChatMethod(QString peerID, QString chatID)
+{
+ for(int i = 0; i < pChatWindows.size(); i++)
+ {
+ if(pChatWindows.at(i)->peerID == peerID && pChatWindows.at(i)->chatID == chatID)
+ {
+ pChatWindows.at(i)->peerReceiverLeftFromChat();
+ }
+ }
}
diff --git a/src/mainwindow.h b/src/mainwindow.h
index 278c2b1..fa791d2 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -3,9 +3,12 @@
#include
#include "kernel/network.hpp"
#include "kernel/handler.hpp"
+#include "chatwindow.hpp"
+#include
#include
#include
#include
+#include
namespace Ui {
@@ -19,18 +22,26 @@ class MainWindow : public QMainWindow
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
+ QVector pChatWindows;
+ void setButtonToWaiting();
+ void setButtonToConnect();
+ inline bool doQuit() const { return mDoQuit; }
public slots:
- void peerReceiverConnected();
+ void peerReceiverAvailable();
private slots:
void on_connectToPeer_clicked();
void slotTimerAlarm();
-
+ void createChatFailedMethod();
+ void createChat(QString peerID, QString chatID);
+ void deleteChatMethod(QString cID);
+ void msgReceivedMethod(QString peerID, QString chatID, QString msgText);
+ void peerReceiverLeftFromChatMethod(QString peerID, QString chatID);
private:
+ void createChatSendDatagram(QString peerReceiver);
Ui::MainWindow *ui;
QTimer *timer;
Network *network;
Handler *handler;
-
- bool receive;
- const QString my_ipv6 = Network::local_ipv6();
+ bool receive = false;
+ bool mDoQuit = false;
};
diff --git a/src/mainwindow.ui b/src/mainwindow.ui
index 7e212b7..4ed2caa 100644
--- a/src/mainwindow.ui
+++ b/src/mainwindow.ui
@@ -29,18 +29,38 @@
Influence
-
- -
+
+
-
- -
-
+
-
+
+
+
+
- Подключиться
+ My IPv6:
+
+
+
+ -
+
+
+
+
+
+ Connect
+
+
+
+ -
+
+
+ true
@@ -49,5 +69,22 @@
-
+
+
+ peerID
+ returnPressed()
+ connectToPeer
+ click()
+
+
+ 122
+ 90
+
+
+ 281
+ 90
+
+
+
+
diff --git a/src/make.sh b/src/make.sh
new file mode 100755
index 0000000..a412cc1
--- /dev/null
+++ b/src/make.sh
@@ -0,0 +1,3 @@
+rm -f Influence
+qmake
+make