+ награды за баллы
- создание - изменение - удаление - отображение
This commit is contained in:
@@ -53,6 +53,7 @@ SOURCES += \
|
|||||||
websocketclient.cpp
|
websocketclient.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
|
action.h \
|
||||||
badge.h \
|
badge.h \
|
||||||
commandprocessor.h \
|
commandprocessor.h \
|
||||||
countermanager.h \
|
countermanager.h \
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// action.h
|
||||||
|
#ifndef ACTION_H
|
||||||
|
#define ACTION_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QVariantMap>
|
||||||
|
|
||||||
|
class Action {
|
||||||
|
public:
|
||||||
|
enum Type { KeyPress, Sound, Notification, File };
|
||||||
|
virtual ~Action() = default;
|
||||||
|
virtual Type type() const = 0;
|
||||||
|
virtual void execute() = 0;
|
||||||
|
virtual QVariantMap toJson() const = 0;
|
||||||
|
virtual void fromJson(const QVariantMap &data) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Конкретные действия
|
||||||
|
class KeyPressAction : public Action {
|
||||||
|
public:
|
||||||
|
Type type() const override { return KeyPress; }
|
||||||
|
void execute() override;
|
||||||
|
QVariantMap toJson() const override;
|
||||||
|
void fromJson(const QVariantMap &data) override;
|
||||||
|
private:
|
||||||
|
int keyCode;
|
||||||
|
Qt::KeyboardModifiers modifiers;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SoundAction : public Action {
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
|
||||||
|
// ... и т.д.
|
||||||
|
#endif // ACTION_H
|
||||||
+113
@@ -1,5 +1,6 @@
|
|||||||
#include "ttw_api.h"
|
#include "ttw_api.h"
|
||||||
#include "qeventloop.h"
|
#include "qeventloop.h"
|
||||||
|
#include "ttw_types.h"
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
@@ -692,3 +693,115 @@ bool TTwAPI::validateTwitchToken(const QString &tokenName,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TTwAPI::getCustomRewards(QVector<TCustomReward*> &rewards, bool onlyManageable)
|
||||||
|
{
|
||||||
|
QString roomId = getRoomId();
|
||||||
|
if (roomId.isEmpty()) return;
|
||||||
|
|
||||||
|
QString url = "channel_points/custom_rewards?broadcaster_id=" + roomId;
|
||||||
|
if (onlyManageable) {
|
||||||
|
url += "&only_manageable_rewards=true";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString response = getTTW(url, m_clientId, true);
|
||||||
|
if (response.isEmpty()) return;
|
||||||
|
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
|
||||||
|
if (!doc.isObject()) return;
|
||||||
|
|
||||||
|
QJsonArray data = doc.object()["data"].toArray();
|
||||||
|
for (const QJsonValue &val : data) {
|
||||||
|
QJsonObject obj = val.toObject();
|
||||||
|
TCustomReward *reward = new TCustomReward;
|
||||||
|
reward->id = obj["id"].toString();
|
||||||
|
reward->title = obj["title"].toString();
|
||||||
|
reward->cost = obj["cost"].toInt();
|
||||||
|
reward->prompt = obj["prompt"].toString();
|
||||||
|
reward->isUserInputRequired = obj["is_user_input_required"].toBool();
|
||||||
|
reward->isEnabled = obj["is_enabled"].toBool();
|
||||||
|
// Для manageable запроса все награды по определению управляемы
|
||||||
|
reward->isManagedByBroadcaster = onlyManageable;
|
||||||
|
rewards.append(reward);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TCustomReward* TTwAPI::createCustomReward(const QString &title, const QString &cost,
|
||||||
|
const QString &prompt, bool isUserInput)
|
||||||
|
{
|
||||||
|
QString roomId = getRoomId();
|
||||||
|
if (roomId.isEmpty()) return nullptr;
|
||||||
|
|
||||||
|
QJsonObject body;
|
||||||
|
body["title"] = title;
|
||||||
|
body["cost"] = cost.toInt();
|
||||||
|
if (!prompt.isEmpty()) body["prompt"] = prompt;
|
||||||
|
body["is_user_input_required"] = isUserInput;
|
||||||
|
// Можно добавить и другие параметры: is_enabled, color и т.д.
|
||||||
|
|
||||||
|
QJsonDocument doc(body);
|
||||||
|
QString response = postTTW("channel_points/custom_rewards?broadcaster_id=" + roomId,
|
||||||
|
m_clientId, doc.toJson(), true);
|
||||||
|
if (response.isEmpty()) return nullptr;
|
||||||
|
|
||||||
|
QJsonDocument respDoc = QJsonDocument::fromJson(response.toUtf8());
|
||||||
|
if (!respDoc.isObject()) return nullptr;
|
||||||
|
|
||||||
|
QJsonArray data = respDoc.object()["data"].toArray();
|
||||||
|
if (data.isEmpty()) return nullptr;
|
||||||
|
|
||||||
|
QJsonObject obj = data[0].toObject();
|
||||||
|
TCustomReward *reward = new TCustomReward;
|
||||||
|
reward->id = obj["id"].toString();
|
||||||
|
reward->title = obj["title"].toString();
|
||||||
|
reward->cost = obj["cost"].toInt();
|
||||||
|
reward->prompt = obj["prompt"].toString();
|
||||||
|
reward->isUserInputRequired = obj["is_user_input_required"].toBool();
|
||||||
|
reward->isEnabled = obj["is_enabled"].toBool();
|
||||||
|
|
||||||
|
return reward;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TTwAPI::updateCustomReward(TCustomReward* reward)
|
||||||
|
{
|
||||||
|
if (!reward) return;
|
||||||
|
QString roomId = getRoomId();
|
||||||
|
if (roomId.isEmpty()) return;
|
||||||
|
|
||||||
|
QJsonObject body;
|
||||||
|
if (!reward->title.isEmpty()) body["title"] = reward->title;
|
||||||
|
body["cost"] = reward->cost;
|
||||||
|
if (!reward->prompt.isEmpty()) body["prompt"] = reward->prompt;
|
||||||
|
body["is_user_input_required"] = reward->isUserInputRequired;
|
||||||
|
body["is_enabled"] = reward->isEnabled;
|
||||||
|
|
||||||
|
QJsonDocument doc(body);
|
||||||
|
patchTTW(QString("channel_points/custom_rewards?broadcaster_id=%1&id=%2")
|
||||||
|
.arg(roomId, reward->id),
|
||||||
|
m_clientId, doc.toJson(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TTwAPI::deleteCustomReward(const QString &id)
|
||||||
|
{
|
||||||
|
QString roomId = getRoomId();
|
||||||
|
if (roomId.isEmpty()) return false;
|
||||||
|
|
||||||
|
deleteTTW("channel_points/custom_rewards?broadcaster_id=" + roomId + "&id=" + id,
|
||||||
|
m_clientId, true);
|
||||||
|
return true; // или проверка HTTP-статуса, но deleteTTW возвращает пустую строку при ошибке
|
||||||
|
}
|
||||||
|
|
||||||
|
void TTwAPI::updateRedemptionStatus(TCustomRewardEvent* event)
|
||||||
|
{
|
||||||
|
// event содержит id награды, id redemption и новый статус (COMPLETED/CANCELED)
|
||||||
|
QString roomId = getRoomId();
|
||||||
|
if (roomId.isEmpty()) return;
|
||||||
|
|
||||||
|
QJsonObject body;
|
||||||
|
body["status"] = event->status; // "COMPLETED" или "CANCELED"
|
||||||
|
|
||||||
|
QJsonDocument doc(body);
|
||||||
|
patchTTW(QString("channel_points/custom_rewards/redemptions?broadcaster_id=%1&reward_id=%2&id=%3")
|
||||||
|
.arg(roomId, event->rewardId, event->redemptionId),
|
||||||
|
m_clientId, doc.toJson(), true);
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
class TCustomReward;
|
class TCustomReward;
|
||||||
class TCustomRewardEvent;
|
class TCustomRewardEvent;
|
||||||
class ChatBadge;
|
struct ChatBadge;
|
||||||
class Emote;
|
class Emote;
|
||||||
|
|
||||||
class TTwAPI : public QObject
|
class TTwAPI : public QObject
|
||||||
@@ -53,14 +53,14 @@ public:
|
|||||||
void delVIP(const QString &id);
|
void delVIP(const QString &id);
|
||||||
|
|
||||||
// Custom Rewards
|
// Custom Rewards
|
||||||
void getCustomRewards(QVector<TCustomReward*> &rewards);
|
void getCustomRewards(QVector<TCustomReward*> &rewards, bool onlyManageable = false);
|
||||||
TCustomReward* createCustomReward(const QString &title,
|
TCustomReward* createCustomReward(const QString &title,
|
||||||
const QString &cost,
|
const QString &cost,
|
||||||
const QString &prompt = "",
|
const QString &prompt = "",
|
||||||
bool isUserInput = false);
|
bool isUserInput = false);
|
||||||
void updateCustomReward(TCustomReward* reward);
|
void updateCustomReward(TCustomReward* reward);
|
||||||
void updateRedemptionStatus(TCustomRewardEvent* event);
|
void updateRedemptionStatus(TCustomRewardEvent* event);
|
||||||
void deleteCustomReward(const QString &id);
|
bool deleteCustomReward(const QString &id);
|
||||||
|
|
||||||
// Пользователи
|
// Пользователи
|
||||||
User getUserByLogin(const QString &login);
|
User getUserByLogin(const QString &login);
|
||||||
|
|||||||
+2
-11
@@ -22,7 +22,7 @@ struct TCustomReward {
|
|||||||
bool isPaused;
|
bool isPaused;
|
||||||
bool isInStock;
|
bool isInStock;
|
||||||
bool shouldRedemptionsSkipRequestQueue;
|
bool shouldRedemptionsSkipRequestQueue;
|
||||||
|
bool isManagedByBroadcaster;
|
||||||
TCustomReward()
|
TCustomReward()
|
||||||
: cost(0)
|
: cost(0)
|
||||||
, isEnabled(true)
|
, isEnabled(true)
|
||||||
@@ -48,21 +48,12 @@ struct TCustomRewardEvent {
|
|||||||
QString userInput;
|
QString userInput;
|
||||||
QString status;
|
QString status;
|
||||||
QDateTime redeemedAt;
|
QDateTime redeemedAt;
|
||||||
|
QString redemptionId;
|
||||||
|
|
||||||
TCustomRewardEvent() {}
|
TCustomRewardEvent() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ChatBadge {
|
|
||||||
QString setId;
|
|
||||||
QString versionId;
|
|
||||||
QString title;
|
|
||||||
QString description;
|
|
||||||
QString smallImageUrl;
|
|
||||||
QString mediumImageUrl;
|
|
||||||
QString largeImageUrl;
|
|
||||||
|
|
||||||
ChatBadge() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Emote {
|
struct Emote {
|
||||||
QString id;
|
QString id;
|
||||||
|
|||||||
+287
-1
@@ -2,6 +2,7 @@
|
|||||||
#include "fcreatenotify.h"
|
#include "fcreatenotify.h"
|
||||||
#include "filemanager.h"
|
#include "filemanager.h"
|
||||||
#include "logmanager.h"
|
#include "logmanager.h"
|
||||||
|
#include "ttw_types.h"
|
||||||
#include "ui_ugeneral.h"
|
#include "ui_ugeneral.h"
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
@@ -187,7 +188,7 @@ void uGeneral::setupButtonIcons() {
|
|||||||
button->setIcon(tabIcons[11]);
|
button->setIcon(tabIcons[11]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (buttonName.contains("edt")) {
|
else if (buttonName.contains("edt") || (buttonName.contains("edit"))) {
|
||||||
button->setIcon(tabIcons[10]);
|
button->setIcon(tabIcons[10]);
|
||||||
}
|
}
|
||||||
else if (buttonName.contains("open")) {
|
else if (buttonName.contains("open")) {
|
||||||
@@ -1805,6 +1806,12 @@ void uGeneral::handleNewMessage(const QString &message)
|
|||||||
if (m_userWidget) {
|
if (m_userWidget) {
|
||||||
m_userWidget->updateStatistics();
|
m_userWidget->updateStatistics();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ui->cbTextToSpeach->isChecked())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
playNotify(msg.isMod, msg.isVIP, msg.isSubscriber);
|
playNotify(msg.isMod, msg.isVIP, msg.isSubscriber);
|
||||||
|
|
||||||
QString processedMessage = processTwitchMessage(msg);
|
QString processedMessage = processTwitchMessage(msg);
|
||||||
@@ -3190,3 +3197,282 @@ void uGeneral::on_btnCounterAtoText_clicked()
|
|||||||
cursor.insertText("|)" + ui->cbCounters->currentText() + "|)");
|
cursor.insertText("|)" + ui->cbCounters->currentText() + "|)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void uGeneral::on_btnCRGet_clicked()
|
||||||
|
{
|
||||||
|
// Очистка старых данных
|
||||||
|
qDeleteAll(m_rewards);
|
||||||
|
m_rewards.clear();
|
||||||
|
|
||||||
|
// 1. Получаем ВСЕ награды канала
|
||||||
|
QVector<TCustomReward*> allRewards;
|
||||||
|
twitchAPI->getCustomRewards(allRewards, false);
|
||||||
|
|
||||||
|
// 2. Получаем только управляемые ботом
|
||||||
|
QVector<TCustomReward*> manageableRewards;
|
||||||
|
twitchAPI->getCustomRewards(manageableRewards, true);
|
||||||
|
|
||||||
|
// 3. Формируем множество ID управляемых наград
|
||||||
|
QSet<QString> manageableIds;
|
||||||
|
for (auto *r : manageableRewards) {
|
||||||
|
manageableIds.insert(r->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Освобождаем manageableRewards (они больше не нужны)
|
||||||
|
qDeleteAll(manageableRewards);
|
||||||
|
manageableRewards.clear();
|
||||||
|
|
||||||
|
// 5. Проставляем флаг для всех наград
|
||||||
|
for (auto *r : allRewards) {
|
||||||
|
r->isManagedByBroadcaster = manageableIds.contains(r->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Сохраняем в член класса
|
||||||
|
m_rewards = allRewards;
|
||||||
|
|
||||||
|
// 7. Заполняем таблицу
|
||||||
|
ui->sgCustomRewards->setRowCount(0);
|
||||||
|
ui->sgCustomRewards->setColumnCount(3);
|
||||||
|
QStringList headers = {"Название", "Цена", "Описание"};
|
||||||
|
ui->sgCustomRewards->setHorizontalHeaderLabels(headers);
|
||||||
|
ui->sgCustomRewards->setRowCount(m_rewards.size());
|
||||||
|
|
||||||
|
for (int row = 0; row < m_rewards.size(); ++row) {
|
||||||
|
TCustomReward *reward = m_rewards[row];
|
||||||
|
|
||||||
|
QTableWidgetItem *nameItem = new QTableWidgetItem(reward->title);
|
||||||
|
nameItem->setData(Qt::UserRole, reward->id);
|
||||||
|
ui->sgCustomRewards->setItem(row, 0, nameItem);
|
||||||
|
|
||||||
|
QTableWidgetItem *costItem = new QTableWidgetItem(QString::number(reward->cost));
|
||||||
|
ui->sgCustomRewards->setItem(row, 1, costItem);
|
||||||
|
|
||||||
|
QTableWidgetItem *descItem = new QTableWidgetItem(reward->prompt);
|
||||||
|
ui->sgCustomRewards->setItem(row, 2, descItem);
|
||||||
|
|
||||||
|
// Устанавливаем цвет фона
|
||||||
|
QColor bgColor = reward->isManagedByBroadcaster ? QColor(144,238,144) : QColor(255,200,200);
|
||||||
|
nameItem->setForeground(bgColor);
|
||||||
|
costItem->setForeground(bgColor);
|
||||||
|
descItem->setForeground(bgColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сбрасываем поля и кнопки
|
||||||
|
ui->edtCRName->clear();
|
||||||
|
ui->edtCRPrompt->clear();
|
||||||
|
ui->sbCRCost->setValue(0);
|
||||||
|
ui->btnCREdit->setEnabled(false);
|
||||||
|
ui->btnCRDelete->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void uGeneral::on_sgCustomRewards_cellClicked(int row, int column)
|
||||||
|
{
|
||||||
|
// Получаем ID награды из первого столбца
|
||||||
|
QTableWidgetItem *idItem = ui->sgCustomRewards->item(row, 0);
|
||||||
|
if (!idItem) return;
|
||||||
|
QString rewardId = idItem->data(Qt::UserRole).toString();
|
||||||
|
|
||||||
|
// Ищем объект награды в m_rewards по ID
|
||||||
|
TCustomReward *reward = nullptr;
|
||||||
|
for (auto *r : m_rewards) {
|
||||||
|
if (r->id == rewardId) {
|
||||||
|
reward = r;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!reward) return; // защита от ошибок
|
||||||
|
|
||||||
|
// Заполняем поля ввода
|
||||||
|
ui->edtCRName->setText(reward->title);
|
||||||
|
ui->edtCRPrompt->setText(reward->prompt);
|
||||||
|
ui->sbCRCost->setValue(reward->cost);
|
||||||
|
|
||||||
|
// Активируем кнопки только если награда управляется ботом
|
||||||
|
bool canEdit = reward->isManagedByBroadcaster;
|
||||||
|
ui->btnCREdit->setEnabled(canEdit);
|
||||||
|
ui->btnCRDelete->setEnabled(canEdit);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void uGeneral::on_sgCustomRewards_cellDoubleClicked(int row, int column)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void uGeneral::on_btnCRAdd_clicked()
|
||||||
|
{
|
||||||
|
// 1. Получаем данные из интерфейса
|
||||||
|
QString title = ui->edtCRName->text().trimmed();
|
||||||
|
QString prompt = ui->edtCRPrompt->text().trimmed();
|
||||||
|
int cost = ui->sbCRCost->value();
|
||||||
|
|
||||||
|
// 2. Простейшая валидация
|
||||||
|
if (title.isEmpty()) {
|
||||||
|
QMessageBox::warning(this, "Ошибка", "Название награды не может быть пустым.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cost <= 0) {
|
||||||
|
QMessageBox::warning(this, "Ошибка", "Стоимость должна быть больше 0.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Вызываем API для создания награды
|
||||||
|
// Предполагаем, что чекбокс "Требуется ввод пользователя" отсутствует, ставим false
|
||||||
|
TCustomReward *newReward = twitchAPI->createCustomReward(title, QString::number(cost), prompt, false);
|
||||||
|
|
||||||
|
if (!newReward) {
|
||||||
|
QMessageBox::critical(this, "Ошибка", "Не удалось создать награду. Проверьте подключение к Twitch и права токена.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Уведомляем пользователя об успехе
|
||||||
|
QMessageBox::information(this, "Успех", "Награда успешно создана.");
|
||||||
|
|
||||||
|
// 5. Очищаем поля ввода (опционально)
|
||||||
|
ui->edtCRName->clear();
|
||||||
|
ui->edtCRPrompt->clear();
|
||||||
|
ui->sbCRCost->setValue(0);
|
||||||
|
|
||||||
|
// 6. Обновляем список наград, чтобы новая появилась в таблице
|
||||||
|
on_btnCRGet_clicked(); // перезагружает и обновляет таблицу
|
||||||
|
|
||||||
|
// Важно: newReward был создан в куче, но после вызова on_btnCRGet_clicked()
|
||||||
|
// старые указатели будут удалены, а новые получены. Указатель newReward
|
||||||
|
// становится недействительным, поэтому не используем его дальше.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void uGeneral::on_btnCREdit_clicked()
|
||||||
|
{
|
||||||
|
// 1. Проверяем, что выбрана строка в таблице
|
||||||
|
int currentRow = ui->sgCustomRewards->currentRow();
|
||||||
|
if (currentRow < 0) {
|
||||||
|
QMessageBox::warning(this, "Ошибка", "Выберите награду для редактирования.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Извлекаем ID награды из первого столбца (хранится в Qt::UserRole)
|
||||||
|
QTableWidgetItem *idItem = ui->sgCustomRewards->item(currentRow, 0);
|
||||||
|
if (!idItem) return;
|
||||||
|
QString rewardId = idItem->data(Qt::UserRole).toString();
|
||||||
|
|
||||||
|
// 3. Ищем объект награды в векторе m_rewards
|
||||||
|
TCustomReward *reward = nullptr;
|
||||||
|
for (auto *r : m_rewards) {
|
||||||
|
if (r->id == rewardId) {
|
||||||
|
reward = r;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!reward) {
|
||||||
|
QMessageBox::critical(this, "Ошибка", "Не удалось найти данные награды.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Проверяем, что награда управляется ботом (кнопка должна быть активна только для таких)
|
||||||
|
if (!reward->isManagedByBroadcaster) {
|
||||||
|
QMessageBox::warning(this, "Ошибка", "Нельзя редактировать награду, созданную не ботом.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Получаем новые значения из полей ввода
|
||||||
|
QString newTitle = ui->edtCRName->text().trimmed();
|
||||||
|
QString newPrompt = ui->edtCRPrompt->text().trimmed();
|
||||||
|
int newCost = ui->sbCRCost->value();
|
||||||
|
|
||||||
|
// 6. Валидация
|
||||||
|
if (newTitle.isEmpty()) {
|
||||||
|
QMessageBox::warning(this, "Ошибка", "Название не может быть пустым.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newCost <= 0) {
|
||||||
|
QMessageBox::warning(this, "Ошибка", "Стоимость должна быть больше 0.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Обновляем данные в объекте
|
||||||
|
reward->title = newTitle;
|
||||||
|
reward->prompt = newPrompt;
|
||||||
|
reward->cost = newCost;
|
||||||
|
|
||||||
|
// 8. Вызываем API для обновления награды
|
||||||
|
twitchAPI->updateCustomReward(reward); // предполагается, что метод возвращает void
|
||||||
|
|
||||||
|
// 9. Обновляем список наград (гарантирует синхронизацию с Twitch)
|
||||||
|
on_btnCRGet_clicked();
|
||||||
|
|
||||||
|
// 10. Уведомляем пользователя об успехе
|
||||||
|
QMessageBox::information(this, "Успех", "Награда обновлена.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void uGeneral::on_btnCRDelete_clicked()
|
||||||
|
{
|
||||||
|
// 1. Проверяем, что выбрана строка в таблице
|
||||||
|
int currentRow = ui->sgCustomRewards->currentRow();
|
||||||
|
if (currentRow < 0) {
|
||||||
|
QMessageBox::warning(this, "Ошибка", "Выберите награду для удаления.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Извлекаем ID награды из первого столбца
|
||||||
|
QTableWidgetItem *idItem = ui->sgCustomRewards->item(currentRow, 0);
|
||||||
|
if (!idItem) return;
|
||||||
|
QString rewardId = idItem->data(Qt::UserRole).toString();
|
||||||
|
|
||||||
|
// 3. Находим объект награды в m_rewards (для проверки управляемости)
|
||||||
|
TCustomReward *reward = nullptr;
|
||||||
|
for (auto *r : m_rewards) {
|
||||||
|
if (r->id == rewardId) {
|
||||||
|
reward = r;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!reward) {
|
||||||
|
QMessageBox::critical(this, "Ошибка", "Не удалось найти данные награды.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Проверяем, что награда управляется ботом
|
||||||
|
if (!reward->isManagedByBroadcaster) {
|
||||||
|
QMessageBox::warning(this, "Ошибка", "Нельзя удалить награду, созданную не ботом.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Запрашиваем подтверждение
|
||||||
|
QString title = reward->title;
|
||||||
|
QMessageBox::StandardButton reply = QMessageBox::question(
|
||||||
|
this,
|
||||||
|
"Подтверждение удаления",
|
||||||
|
QString("Вы уверены, что хотите удалить награду \"%1\"?").arg(title),
|
||||||
|
QMessageBox::Yes | QMessageBox::No
|
||||||
|
);
|
||||||
|
|
||||||
|
if (reply != QMessageBox::Yes) {
|
||||||
|
return; // пользователь отменил
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Вызываем API для удаления
|
||||||
|
bool success = twitchAPI->deleteCustomReward(rewardId); // предполагаем, что метод возвращает bool
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
QMessageBox::critical(this, "Ошибка", "Не удалось удалить награду. Проверьте подключение к Twitch и права токена.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Уведомляем об успехе
|
||||||
|
QMessageBox::information(this, "Успех", "Награда успешно удалена.");
|
||||||
|
|
||||||
|
// 8. Обновляем список наград (удалённая исчезнет)
|
||||||
|
on_btnCRGet_clicked();
|
||||||
|
|
||||||
|
// 9. Сбрасываем поля ввода и отключаем кнопки (так как выбор пропал)
|
||||||
|
ui->edtCRName->clear();
|
||||||
|
ui->edtCRPrompt->clear();
|
||||||
|
ui->sbCRCost->setValue(0);
|
||||||
|
ui->btnCREdit->setEnabled(false);
|
||||||
|
ui->btnCRDelete->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-1
@@ -354,6 +354,18 @@ private slots:
|
|||||||
|
|
||||||
void on_btnCounterAtoText_clicked();
|
void on_btnCounterAtoText_clicked();
|
||||||
|
|
||||||
|
void on_btnCRGet_clicked();
|
||||||
|
|
||||||
|
void on_sgCustomRewards_cellClicked(int row, int column);
|
||||||
|
|
||||||
|
void on_sgCustomRewards_cellDoubleClicked(int row, int column);
|
||||||
|
|
||||||
|
void on_btnCRAdd_clicked();
|
||||||
|
|
||||||
|
void on_btnCREdit_clicked();
|
||||||
|
|
||||||
|
void on_btnCRDelete_clicked();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
// Установка статуса подключения к Twitch
|
// Установка статуса подключения к Twitch
|
||||||
void setTwitchConnected(bool connected);
|
void setTwitchConnected(bool connected);
|
||||||
@@ -383,7 +395,7 @@ private:
|
|||||||
QList<TimerInfo> m_timers; // Список таймеров
|
QList<TimerInfo> m_timers; // Список таймеров
|
||||||
int m_nextTimerId = 1; // Следующий ID таймера
|
int m_nextTimerId = 1; // Следующий ID таймера
|
||||||
bool m_isTwitchConnected = false; // Статус подключения к Twitch
|
bool m_isTwitchConnected = false; // Статус подключения к Twitch
|
||||||
|
QVector<TCustomReward*> m_rewards;
|
||||||
// Менеджеры веб-серверов
|
// Менеджеры веб-серверов
|
||||||
QList<HttpServer*> m_notificationServers;
|
QList<HttpServer*> m_notificationServers;
|
||||||
QList<HttpServerChat*> m_chatServers;
|
QList<HttpServerChat*> m_chatServers;
|
||||||
|
|||||||
+154
-3
@@ -42,7 +42,7 @@
|
|||||||
<enum>Qt::LeftToRight</enum>
|
<enum>Qt::LeftToRight</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>2</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="tabsClosable">
|
<property name="tabsClosable">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
@@ -878,7 +878,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="cbTextToSpeach">
|
<widget class="QCheckBox" name="cbTextToSpeach">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Озвучить после !!!</string>
|
<string>Игнорировать все сообщения</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -1087,7 +1087,158 @@
|
|||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Навыки</string>
|
<string>Навыки</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_tab_skills"/>
|
<layout class="QVBoxLayout" name="verticalLayout_tab_skills">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_17">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox_12">
|
||||||
|
<property name="title">
|
||||||
|
<string>Баллы канала</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_28">
|
||||||
|
<item>
|
||||||
|
<widget class="QTableWidget" name="sgCustomRewards"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_18">
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_26">
|
||||||
|
<property name="text">
|
||||||
|
<string>Название:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="edtCRName"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_27">
|
||||||
|
<property name="text">
|
||||||
|
<string>Описание:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="edtCRPrompt"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_28">
|
||||||
|
<property name="text">
|
||||||
|
<string>Цена:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="sbCRCost">
|
||||||
|
<property name="maximum">
|
||||||
|
<number>9999999</number>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<number>1000</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_11">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_19">
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btnCRAdd">
|
||||||
|
<property name="text">
|
||||||
|
<string>Добавить</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btnCREdit">
|
||||||
|
<property name="text">
|
||||||
|
<string>Изменить</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btnCRDelete">
|
||||||
|
<property name="text">
|
||||||
|
<string>Удалить</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btnCRGet">
|
||||||
|
<property name="text">
|
||||||
|
<string>Обновить</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_4">
|
||||||
|
<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>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox_13">
|
||||||
|
<property name="title">
|
||||||
|
<string>Действия</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_30">
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_29">
|
||||||
|
<item>
|
||||||
|
<widget class="QListWidget" name="listWidget_2"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_7">
|
||||||
|
<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>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab_5">
|
<widget class="QWidget" name="tab_5">
|
||||||
<property name="icon" stdset="0">
|
<property name="icon" stdset="0">
|
||||||
|
|||||||
Reference in New Issue
Block a user