266 lines
13 KiB
C++
266 lines
13 KiB
C++
/*****************************************************************************
|
|
* xmppbot Simple Unix Socket based XMPP bot
|
|
* Copyright (C) 2021 Syping
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* This software is provided as-is, no warranties are given to you, we are not
|
|
* responsible for anything with use of the software, you are self responsible.
|
|
*****************************************************************************/
|
|
|
|
#include <QCommandLineParser>
|
|
#include <QCoreApplication>
|
|
#include <QLocalServer>
|
|
#include <QLocalSocket>
|
|
#include <QTextStream>
|
|
#include <QSettings>
|
|
#include <QProcess>
|
|
#include <QTimer>
|
|
#include <QFile>
|
|
|
|
#include "xmppbot.h"
|
|
#include "xmppbotlua.h"
|
|
#include "xmppbotluathread.h"
|
|
#include "xmppsocket.h"
|
|
|
|
#include "QXmppClient.h"
|
|
#include "QXmppMessage.h"
|
|
#include "QXmppPresence.h"
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
QCoreApplication app(argc, argv);
|
|
app.setApplicationName(QLatin1String("xmppbot"));
|
|
app.setApplicationVersion(QLatin1String("0.6.1"));
|
|
|
|
QCommandLineParser commandLineParser;
|
|
commandLineParser.addPositionalArgument(QLatin1String("config"), QCoreApplication::translate("xmppbot", "Configuration file."));
|
|
commandLineParser.addHelpOption();
|
|
commandLineParser.addVersionOption();
|
|
commandLineParser.process(app);
|
|
|
|
QString settingsPath = QLatin1String("settings.ini");
|
|
const QStringList args = commandLineParser.positionalArguments();
|
|
if (args.length() == 1) {
|
|
const QString a_settingsPath = args.at(0);
|
|
if (QFile::exists(a_settingsPath)) {
|
|
settingsPath = a_settingsPath;
|
|
}
|
|
else {
|
|
QTextStream(stderr) << QLatin1String("xmppbot: ") << a_settingsPath << QLatin1String(" not found!") << xendl;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
QXmppClient client;
|
|
app.setProperty("XmppClient", QVariant::fromValue<QXmppClient*>(&client));
|
|
|
|
bool loginSet = false;
|
|
QString jid, jpw, script;
|
|
QHash<QString, QString> h_msg;
|
|
QHash<QString, QString> h_lua;
|
|
QHash<QString, QString> h_run;
|
|
if (QFile::exists(settingsPath)) {
|
|
QSettings settings(settingsPath, QSettings::IniFormat);
|
|
for (const QString &group : settings.childGroups()) {
|
|
settings.beginGroup(group);
|
|
if (group == QLatin1String("xmppbot")) {
|
|
script = settings.value(QLatin1String("Script"), QString()).toString();
|
|
}
|
|
else {
|
|
for (const QString &key : settings.childKeys()) {
|
|
if (key == QLatin1String("Password")) {
|
|
if (!loginSet) {
|
|
jid = group;
|
|
const QString instance = settings.value(QLatin1String("Instance"), QString()).toString();
|
|
if (!instance.isEmpty()) {
|
|
jid += QLatin1String("/") + instance;
|
|
}
|
|
jpw = settings.value(key, QString()).toString();
|
|
loginSet = true;
|
|
}
|
|
else {
|
|
QTextStream(stderr) << "xmppbot: Login password can only be set once!" << xendl;
|
|
return 1;
|
|
}
|
|
}
|
|
if (key == QLatin1String("Incoming")) {
|
|
const QString incoming = settings.value("Incoming", QString()).toString();
|
|
if (incoming.startsWith(QLatin1String("message:"))) {
|
|
QTextStream(stderr) << QLatin1String("xmppbot: Account message incoming ") << group << QLatin1String(" initialised") << xendl;
|
|
h_msg.insert(group, incoming.mid(8));
|
|
}
|
|
if (incoming.startsWith(QLatin1String("lua:"))) {
|
|
QTextStream(stderr) << QLatin1String("xmppbot: Account lua incoming ") << group << QLatin1String(" initialised") << xendl;
|
|
h_lua.insert(group, incoming.mid(4));
|
|
}
|
|
if (incoming.startsWith(QLatin1String("run:"))) {
|
|
QTextStream(stderr) << QLatin1String("xmppbot: Account run incoming ") << group << QLatin1String(" initialised") << xendl;
|
|
h_run.insert(group, incoming.mid(4));
|
|
}
|
|
}
|
|
if (key == QLatin1String(XmppSocketType)) {
|
|
XmppSocket *xmppSocket = new XmppSocket(&client, jid, group);
|
|
#ifdef Q_OS_UNIX
|
|
const QString permission = settings.value(QLatin1String("SocketPermission"), QString()).toString();
|
|
if (permission == QLatin1String("UG") || permission == QLatin1String("UserGroup")) {
|
|
xmppSocket->setSocketOptions(QLocalServer::UserAccessOption | QLocalServer::GroupAccessOption);
|
|
}
|
|
if (permission == QLatin1String("UO") || permission == QLatin1String("UserOther")) {
|
|
xmppSocket->setSocketOptions(QLocalServer::UserAccessOption | QLocalServer::OtherAccessOption);
|
|
}
|
|
if (permission == QLatin1String("U") || permission == QLatin1String("User")) {
|
|
xmppSocket->setSocketOptions(QLocalServer::UserAccessOption);
|
|
}
|
|
if (permission == QLatin1String("GO") || permission == QLatin1String("GroupOther")) {
|
|
xmppSocket->setSocketOptions(QLocalServer::GroupAccessOption | QLocalServer::OtherAccessOption);
|
|
}
|
|
if (permission == QLatin1String("G") || permission == QLatin1String("Group")) {
|
|
xmppSocket->setSocketOptions(QLocalServer::GroupAccessOption);
|
|
}
|
|
if (permission == QLatin1String("O") || permission == QLatin1String("Other")) {
|
|
xmppSocket->setSocketOptions(QLocalServer::OtherAccessOption);
|
|
}
|
|
if (permission == QLatin1String("A") || permission == QLatin1String("All") || permission == QLatin1String("UGO") || permission == QLatin1String("UserGroupOther")) {
|
|
xmppSocket->setSocketOptions(QLocalServer::WorldAccessOption);
|
|
}
|
|
#endif
|
|
const QString socketPath = settings.value(key, QString()).toString();
|
|
bool listen = xmppSocket->listen(socketPath);
|
|
#ifdef Q_OS_UNIX
|
|
if (!listen) {
|
|
QLocalServer::removeServer(socketPath);
|
|
listen = xmppSocket->listen(socketPath);
|
|
}
|
|
#endif
|
|
if (listen) {
|
|
QTextStream(stderr) << QLatin1String("xmppbot: Account socket ") << group << QLatin1String(" initialised") << xendl;
|
|
}
|
|
else {
|
|
delete xmppSocket;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
settings.endGroup();
|
|
}
|
|
}
|
|
else {
|
|
QTextStream(stderr) << QLatin1String("xmppbot: Can't initialise without settings.ini!") << xendl;
|
|
return 1;
|
|
}
|
|
|
|
if (jid.isEmpty() || jpw.isEmpty()) {
|
|
QTextStream(stderr) << QLatin1String("xmppbot: Can't initialise without XMPP account!") << xendl;
|
|
return 1;
|
|
}
|
|
|
|
QTextStream(stderr) << QLatin1String("xmppbot: Account login ") << jid << QLatin1String(" initialised") << xendl;
|
|
|
|
XmppBotLuaThread xmppBotLuaGlobalThread(script, QLatin1String("jidInitialised"), QVariantList() << jid, true);
|
|
if (!script.isEmpty() && QFile::exists(script)) {
|
|
xmppBotLuaGlobalThread.start();
|
|
}
|
|
|
|
QObject::connect(&client, &QXmppClient::stateChanged, [&](QXmppClient::State state) {
|
|
switch (state) {
|
|
case QXmppClient::ConnectedState: {
|
|
QTextStream(stderr) << QLatin1String("xmppbot: Account ") << jid << QLatin1String(" connected") << xendl;
|
|
QXmppPresence xmppPresence(QXmppPresence::Available);
|
|
client.setClientPresence(xmppPresence);
|
|
if (xmppBotLuaGlobalThread.isRunning()) {
|
|
const QString lua_function = QLatin1String("jidConnected");
|
|
const QVariantList lua_args = QVariantList() << jid;
|
|
QMetaObject::invokeMethod(&xmppBotLuaGlobalThread, "executeLuaFunction", Qt::QueuedConnection, Q_ARG(QString, lua_function), Q_ARG(QVariantList, lua_args));
|
|
}
|
|
break;
|
|
}
|
|
case QXmppClient::ConnectingState:
|
|
break;
|
|
case QXmppClient::DisconnectedState:
|
|
QTextStream(stderr) << QLatin1String("xmppbot: Account ") << jid << QLatin1String(" disconnected") << xendl;
|
|
QTimer::singleShot(5000, &client, [&]() {
|
|
client.connectToServer(jid, jpw);
|
|
});
|
|
if (xmppBotLuaGlobalThread.isRunning()) {
|
|
const QString lua_function = QLatin1String("jidDisconnected");
|
|
const QVariantList lua_args = QVariantList() << jid;
|
|
QMetaObject::invokeMethod(&xmppBotLuaGlobalThread, "executeLuaFunction", Qt::QueuedConnection, Q_ARG(QString, lua_function), Q_ARG(QVariantList, lua_args));
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
});
|
|
|
|
QObject::connect(&client, &QXmppClient::messageReceived, [&](const QXmppMessage &xmppMessage) {
|
|
const QString body = xmppMessage.body();
|
|
if (body.isEmpty())
|
|
return;
|
|
const QString from = xmppMessage.from();
|
|
QString from_jid;
|
|
for (const QChar &val : from) {
|
|
if (val == '/')
|
|
break;
|
|
from_jid += val;
|
|
}
|
|
const QString msg = h_msg.value(from_jid, QString());
|
|
if (!msg.isEmpty()) {
|
|
QXmppMessage xmppMessage(jid, from, msg);
|
|
client.sendPacket(xmppMessage);
|
|
}
|
|
const QString lua = h_lua.value(from_jid, QString());
|
|
if (!lua.isEmpty()) {
|
|
XmppBotLuaThread *xmppBotLuaThread = new XmppBotLuaThread(lua, QLatin1String("messageReceived"), QVariantList() << from << xmppMessage.to() << xmppMessage.body());
|
|
QObject::connect(xmppBotLuaThread, &XmppBotLuaThread::finished, xmppBotLuaThread, &XmppBotLuaThread::deleteLater);
|
|
xmppBotLuaThread->start();
|
|
}
|
|
const QString run = h_run.value(from_jid, QString());
|
|
if (!run.isEmpty()) {
|
|
qint64 pid;
|
|
bool isStarted = QProcess::startDetached(run, QStringList() << from << xmppMessage.to() << xmppMessage.body(), QString(), &pid);
|
|
if (isStarted) {
|
|
QTextStream(stderr) << QLatin1String("xmppbot: Account ") << from_jid << QLatin1String(" executed pid ") << pid << xendl;
|
|
}
|
|
}
|
|
if (xmppBotLuaGlobalThread.isRunning()) {
|
|
const QString lua_function = QLatin1String("messageReceived");
|
|
const QVariantList lua_args = QVariantList() << from << xmppMessage.to() << xmppMessage.body();
|
|
QMetaObject::invokeMethod(&xmppBotLuaGlobalThread, "executeLuaFunction", Qt::QueuedConnection, Q_ARG(QString, lua_function), Q_ARG(QVariantList, lua_args));
|
|
}
|
|
});
|
|
|
|
QObject::connect(&client, &QXmppClient::presenceReceived, [&](const QXmppPresence &xmppPresence) {
|
|
const QString from = xmppPresence.from();
|
|
QString from_jid;
|
|
for (const QChar &val : from) {
|
|
if (val == '/')
|
|
break;
|
|
from_jid += val;
|
|
}
|
|
const QString lua = h_lua.value(from_jid, QString());
|
|
if (!lua.isEmpty()) {
|
|
XmppBotLuaThread *xmppBotLuaThread = new XmppBotLuaThread(lua, QLatin1String("presenceReceived"), QVariantList() << from << static_cast<int>(xmppPresence.type()) << static_cast<int>(xmppPresence.availableStatusType()) << xmppPresence.statusText());
|
|
QObject::connect(xmppBotLuaThread, &XmppBotLuaThread::finished, xmppBotLuaThread, &XmppBotLuaThread::deleteLater);
|
|
xmppBotLuaThread->start();
|
|
}
|
|
if (xmppBotLuaGlobalThread.isRunning()) {
|
|
const QString lua_function = QLatin1String("presenceReceived");
|
|
const QVariantList lua_args = QVariantList() << from << static_cast<int>(xmppPresence.type()) << static_cast<int>(xmppPresence.availableStatusType()) << xmppPresence.statusText();
|
|
QMetaObject::invokeMethod(&xmppBotLuaGlobalThread, "executeLuaFunction", Qt::QueuedConnection, Q_ARG(QString, lua_function), Q_ARG(QVariantList, lua_args));
|
|
}
|
|
});
|
|
|
|
client.connectToServer(jid, jpw);
|
|
|
|
return app.exec();
|
|
}
|