tim-gromeyer / VoiceAssistant

Empower Your Voice, Secure Your Privacy - Experience VoiceAssistant, Your Customizable Offline Voice Assistant!
https://tim-gromeyer.github.io/VoiceAssistant/
GNU General Public License v3.0
10 stars 2 forks source link

Slow down voice temporarily #2

Closed github-actions[bot] closed 1 year ago

github-actions[bot] commented 1 year ago

https://github.com/tim-gromeyer/VoiceAssistant/blob/d7d2f8e2cdb8f77eee5a2baf527a0a23098ce0b8/src/jokes.cpp#L78


#include "jokes.h"
#include "global.h"
#include "mainwindow.h"
#include "utils.h"

#include <QCoreApplication>
#include <QDataStream>
#include <QEventLoop>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QTimer>

#include <chrono>

using namespace std::chrono_literals;
using namespace utils::literals;

inline QDataStream &operator<<(QDataStream &out, const Jokes::Joke &j)
{
    out << j.joke;
    out << j.delivery;
    out << j.error;
    out << j.isSingle;

    return out;
}
inline QDataStream &operator>>(QDataStream &in, Jokes::Joke &j)
{
    in >> j.joke;
    in >> j.delivery;
    in >> j.error;
    in >> j.isSingle;

    return in;
}

Jokes::Jokes(QObject *parent)
    : QObject(parent)
    , manager(new QNetworkAccessManager(this))
{
#ifdef QT5
    qRegisterMetaTypeStreamOperators<Jokes::Joke>("Joke");
#endif
}

void Jokes::setup()
{
    qDebug() << "Jokes: setup";
    static const QStringList jokeLangs(
        {STR("cs"), STR("de"), STR("en"), STR("es"), STR("fr"), STR("pt")});
    static const QStringList uiLangs = QLocale::system().uiLanguages();

    jokeLang = std::accumulate(uiLangs.begin(),
                               uiLangs.end(),
                               QString(QLatin1String()),
                               [](const QString &a, const QString &b) {
                                   const QString lang = b.right(2);
                                   return jokeLangs.contains(lang) ? lang : a;
                               });

    if (jokeLang.isEmpty()) {
        qCritical() << tr("No jokes are available for your language.\nYour languages: %1\nJoke "
                          "languages: %2")
                           .arg(uiLangs.join(STR(", ")), jokeLangs.join(STR(", ")));
        return;
    }

    loadJokes();
    fetchJokes();
    qDebug() << "Jokes: Setup finished";
}

void Jokes::tellJoke()
{
    qDebug() << "Jokes: Telling a joke. Cached amount of jokes:" << jokes.size();
    // TODO: Slow down voice temporarily
    if (jokes.isEmpty()) {
        fetchJokes();
        if (jokes.isEmpty()) {
            MainWindow::say(tr("Sorry, something went wrong"));
            return;
        }
    }

    Joke joke = jokes.takeFirst();
    previousJokes.append(joke);

    qDebug() << joke.joke << joke.delivery;
    if (jokes.size() == 1)
        QTimer::singleShot(3s, this, &Jokes::fetchJokes);

    if (joke.isSingle) {
        MainWindow::say(joke.joke);
        return;
    }

    MainWindow::sayAndWait(joke.joke);

    QEventLoop loop(this);

    QTimer timer(this);
    timer.setInterval(700ms);
    timer.setSingleShot(true);
    connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
    timer.start();
    loop.exec();

    MainWindow::say(joke.delivery);
}

Jokes::Joke parseJson(const QByteArray &json)
{
    Jokes::Joke joke;
    QJsonDocument doc = QJsonDocument::fromJson(json);
    QJsonObject obj = doc.object();

    if (obj[STR("error")].toBool(false)) {
        joke.error = true;
        return joke;
    }
    joke.isSingle = obj[STR("type")].toString() == L1("single");
    if (joke.isSingle) {
        joke.joke = obj[STR("joke")].toString();
    } else {
        joke.joke = obj[STR("setup")].toString();
        joke.delivery = obj[STR("delivery")].toString();
    }

    return joke;
}

void Jokes::fetchJokes()
{
    qDebug() << "Jokes: Fetching jokes";
    int tries = 0;

    while (jokes.size() <= 2 && tries <= 5) {
        ++tries;

        QUrl url(STR("https://v2.jokeapi.dev/joke/Any?safe-mode&lang=%1").arg(jokeLang));
        reply = manager->get(QNetworkRequest(url));

        QEventLoop loop(this);
        connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
        loop.exec();

        if (reply->error() != QNetworkReply::NoError) {
            qCritical() << tr("Could not fetch joke: %1").arg(reply->errorString());
            return;
        }

        QByteArray data = reply->readAll();
        Joke joke = parseJson(data);
        if (joke.error)
            continue;

        if (!previousJokes.contains(joke))
            jokes.append(joke);
    }

    qDebug() << "Jokes: Fetching jokes completed. Number of cached jokes:" << jokes.size();
}

QString jokeFile = dir::dataDir() + QStringLiteral("/jokes.data");

void Jokes::loadJokes()
{
    QDir().mkpath(dir::dataDir());
    QFile f(jokeFile);
    if (!f.open(QIODevice::ReadOnly))
        return;

    QDataStream in(&f);
    while (!in.atEnd()) {
        Joke j;
        in >> j;
        if (!jokes.contains(j))
            jokes.append(j);
    }
    f.close();
}

void Jokes::saveJokes()
{
    QFile f(jokeFile, this);
    if (!f.open(QIODevice::WriteOnly))
        return;

    QDataStream out(&f);
    for (const Joke &j : qAsConst(jokes)) {
        out << j;
    }
}

Jokes::~Jokes()
{
    saveJokes();
}
tim-gromeyer commented 1 year ago

Requires #4

github-actions[bot] commented 1 year ago

Closed in 99482e1d1bd3a85536665e7f705d7a4410b39c9f