Skip to content

Commit

Permalink
🐝 added osu! api support
Browse files Browse the repository at this point in the history
Added osu! API
  • Loading branch information
Sclafus authored Dec 7, 2023
2 parents e7be442 + e19b1f2 commit 8b59ae9
Show file tree
Hide file tree
Showing 19 changed files with 451 additions and 101 deletions.
4 changes: 0 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,6 @@ endif()
target_include_directories(miraya PRIVATE
${FORMS_DIR}
${SOURCE_DIR}
${SOURCE_TWITCH_DIR}
${SOURCE_OSU_DIR}
${SOURCE_UTILS_DIR}
${SOURCE_SETUPWIZARD_DIR}
)
# =========================== linking libraries ===========================
target_link_libraries(miraya PRIVATE Qt6::Core Qt6::Widgets Qt6::WebSockets Qt6::Network)
71 changes: 69 additions & 2 deletions forms/preferences.ui
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@
<normaloff>:/resources/images/logo_gosumemory.ico</normaloff>:/resources/images/logo_gosumemory.ico</iconset>
</property>
</item>
<item>
<property name="text">
<string>osu! API</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<normaloff>:/resources/images/logo_osu.png</normaloff>:/resources/images/logo_osu.png</iconset>
</property>
</item>
<item>
<property name="text">
<string>Twitch</string>
Expand All @@ -61,7 +70,7 @@
</item>
<item>
<property name="text">
<string>Osu! IRC</string>
<string>osu! IRC</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
Expand Down Expand Up @@ -161,6 +170,64 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="osuapiPage">
<layout class="QGridLayout" name="gridLayout_7">
<item row="2" column="1">
<widget class="QLineEdit" name="osuapiClientSecretLineEdit">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="osuapiClientIdLineEdit">
<property name="inputMethodHints">
<set>Qt::ImhDigitsOnly</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="osuapiClientIdLabel">
<property name="text">
<string>Client ID</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="osuapiClientSecretLabel">
<property name="text">
<string>Client Secret</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<spacer name="verticalSpacer_11">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" colspan="2">
<spacer name="verticalSpacer_10">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="twitchPage">
<layout class="QGridLayout" name="gridLayout_4">
<item row="3" column="1">
Expand Down Expand Up @@ -325,7 +392,7 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="page">
<widget class="QWidget" name="backupPage">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QPushButton" name="backupBtn">
Expand Down
58 changes: 58 additions & 0 deletions forms/setupwizard.ui
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,64 @@
</item>
</layout>
</widget>
<widget class="QWizardPage" name="osuapi">
<property name="title">
<string>osu! API</string>
</property>
<property name="subTitle">
<string>This will help miraya to grab data from beatmaps when they are requested.</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="0" rowspan="2">
<layout class="QFormLayout" name="osuapiFormLayout">
<item row="0" column="0">
<widget class="QLabel" name="osuapiClientIdLabel">
<property name="text">
<string>osu! Client ID</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="osuapiClientIdLineEdit">
<property name="inputMethodHints">
<set>Qt::ImhDigitsOnly</set>
</property>
<property name="maxLength">
<number>6</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="osuapiClientSecretLabel">
<property name="text">
<string>osu! Client Secret</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="osuapiClientSecretLineEdit">
<property name="inputMethodHints">
<set>Qt::ImhHiddenText|Qt::ImhNoAutoUppercase|Qt::ImhNoPredictiveText|Qt::ImhSensitiveData</set>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="osuapiExplanationLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Go to your &lt;a href=&quot;https://osu.ppy.sh/home/account/edit&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#4285f4;&quot;&gt;osu! account settings&lt;/span&gt;&lt;/a&gt; and create a new OAuth application (at the bottom of the page). &lt;/p&gt;&lt;p&gt;You can chose whatever name you want, and there is no need to put any application callback URL.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWizardPage" name="twitch">
<property name="title">
<string>Twitch Setup</string>
Expand Down
9 changes: 4 additions & 5 deletions src/backup/backup.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@
#include <QObject>
#include <QSettings>

class Backup : public QObject
class Backup
{
Q_OBJECT
public:
static void backup(QString filePath, bool includeSensitiveInfo);
static void restore(QString filePath);
public:
static void backup(QString filePath, bool includeSensitiveInfo);
static void restore(QString filePath);
};

#endif // BACKUP_H
2 changes: 1 addition & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ int main(int argc, char **argv) {
app.setOrganizationName("miraya");
app.setOrganizationDomain("github.com/MirayaProject");
app.setApplicationName("bot");
app.setApplicationVersion("1.2.0");
app.setApplicationVersion("1.3.0-alpha.1");
#ifdef Q_OS_LINUX
app.setWindowIcon(QIcon(":/resources/logo/logo.png"));
#endif
Expand Down
17 changes: 14 additions & 3 deletions src/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ MainWindow::MainWindow(QWidget *parent) :
updater = new Updater(this);
updater->checkVersion();

osuApi = new OsuApi();

setupSignals();
if(!settings.value("setup/completed").toBool()){
on_actionStart_Setup_triggered();
Expand Down Expand Up @@ -143,9 +145,18 @@ void MainWindow::onGosumemoryClientMessageReceived(GosuMemoryDataWrapper message
void MainWindow::onTwitchClientMessageReceived(TwitchDataWrapper message)
{
// TODO: this should not be here
for (auto val : Utils().getOsuBeatmapUrls(message.getMessage())) {
qDebug() << "[MainWindow] Osu beatmap url: " << val;
osuIrcClient->sendMap(QUrl(val), message);
for (QString url : Utils::getOsuBeatmapUrls(message.getMessage())) {
qDebug() << "[MainWindow] Osu beatmap url: " << url;

int beatmapId = Utils::getBeatmapIdFromOsuBeatmapLink(url);
if (beatmapId > 0 && osuApi->isValid()) {
// TODO: also this should not be here
QJsonObject mapData = osuApi->getBeatmapInfo(beatmapId);
osuIrcClient->sendMap(mapData, message);
}
else {
osuIrcClient->sendMap(QUrl(url), message);
}
}

QLabel* label = getTwitchChatMessage(message.getUsername(), message.getMessage());
Expand Down
3 changes: 2 additions & 1 deletion src/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "twitchdatawrapper.h"
#include "updater.h"
#include "utils.h"
#include "osu/api/osuapi.h"


namespace Ui {
Expand All @@ -41,7 +42,6 @@ class MainWindow : public QMainWindow
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();


private slots:
void on_btnStart_clicked();
void on_actionStart_Setup_triggered();
Expand Down Expand Up @@ -78,6 +78,7 @@ class MainWindow : public QMainWindow
GosumemoryClient *gosumemoryClient;
OsuIrcClient *osuIrcClient;
Updater *updater;
OsuApi *osuApi;

QLabel *ircConnectionLabel;
QLabel *twitchConnectionLabel;
Expand Down
42 changes: 42 additions & 0 deletions src/osu/api/auth/clientCredentialsFlow.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include "clientCredentialsFlow.h"

QJsonObject ClientCredentialsFlow::getToken(QString clientId, QString clientSecret, QString oAuthUrl)
{
qDebug() << "[ClientCredentialsFlow] getToken";
QNetworkAccessManager manager;
QNetworkRequest request((QUrl(oAuthUrl)));

request.setHeader(
QNetworkRequest::ContentTypeHeader,
"application/x-www-form-urlencoded"
);
request.setHeader(
QNetworkRequest::UserAgentHeader,
"Miraya"
);

QUrlQuery params;
params.addQueryItem("client_id", clientId);
params.addQueryItem("client_secret", clientSecret);
params.addQueryItem("grant_type", "client_credentials");
params.addQueryItem("scope", "public");

QNetworkReply *reply = manager.post(request, params.toString(QUrl::FullyEncoded).toUtf8());

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

int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();

qDebug() << "[ClientCredentialsFlow] HTTP Status code: " << httpStatusCode;
if (httpStatusCode == 200) {
QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
QJsonObject obj = doc.object();
return obj;
}
qDebug() << "[ClientCredentialsFlow] Failed to get token";
QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
qDebug() << QString(doc.toJson(QJsonDocument::Compact));
return doc.object();
}
24 changes: 24 additions & 0 deletions src/osu/api/auth/clientCredentialsFlow.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef CLIENTCREDENTIALSFLOW_H
#define CLIENTCREDENTIALSFLOW_H

#include <QUrl>
#include <QDebug>
#include <QEventLoop>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrlQuery>
#include <QJsonDocument>
#include <QJsonObject>

class ClientCredentialsFlow
{
public:
static QJsonObject getToken(
QString clientId,
QString clientSecret,
QString oAuthUrl = QString("https://osu.ppy.sh/oauth/token")
);
};

#endif // CLIENTCREDENTIALSFLOW_H
58 changes: 58 additions & 0 deletions src/osu/api/osuapi.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#include "osuapi.h"

OsuApi::OsuApi()
{
qDebug() << "[OsuApi] Init";
QSettings settings;
clientId = settings.value("osuapi/clientId").toString();
clientSecret = settings.value("osuapi/clientSecret").toString();
oAuthUrl = "https://osu.ppy.sh/oauth/token";

if (!(clientId.isEmpty() || clientSecret.isEmpty())) {
token = ClientCredentialsFlow::getToken(clientId, clientSecret, oAuthUrl);
}
else {
qDebug() << "[OsuApi] Client ID or Client Secret are empty";
}
}

QJsonObject OsuApi::getBeatmapInfo(int beatmapId)
{
// TODO: A lot can be split into smaller functions
// TODO: also, the QEventLoop is making everything synchronous, which is suboptimal to say the least.
qDebug() << "[OsuApi] getBeatmapInfo";
QNetworkAccessManager manager;
QUrl url(QString("https://osu.ppy.sh/api/v2/beatmaps/%1").arg(beatmapId));

QNetworkRequest request(url);

QString accessToken = token["access_token"].toString();

request.setRawHeader(
QByteArray("Authorization"),
(QString("Bearer %1").arg(accessToken)).toUtf8()
);

QNetworkReply *reply = manager.get(request);

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

qDebug() << "[OsuApi] get request done";
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug() << "[OsuApi] HTTP Status code: " << httpStatusCode;
if (httpStatusCode == 200) {
QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
QJsonObject obj = doc.object();
qDebug() << "[OsuApi] Beatmap info: " << obj;
return obj;
}
qDebug() << "[OsuApi] Error: " << reply->errorString();
return QJsonObject();
}

bool OsuApi::isValid()
{
return !(clientId.isEmpty() || clientSecret.isEmpty());
}
Loading

0 comments on commit 8b59ae9

Please sign in to comment.