summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorilotterytea <iltsu@alright.party>2025-04-17 01:56:12 +0500
committerilotterytea <iltsu@alright.party>2025-04-17 01:56:12 +0500
commit3355ee655fde11f3bb65037d4090a27e7acb37b1 (patch)
tree562679575319d6258152dc8c3fb20f6baa7f8162
parent61d1bb2d4b56dc1bbf4c5a43fbe3bbaf3430e25d (diff)
feat: !notify in lua
-rw-r--r--bot/src/commands/command.cpp2
-rw-r--r--bot/src/modules/notify.hpp131
-rw-r--r--luamods/notify.lua211
3 files changed, 211 insertions, 133 deletions
diff --git a/bot/src/commands/command.cpp b/bot/src/commands/command.cpp
index 55fac64..410d5ba 100644
--- a/bot/src/commands/command.cpp
+++ b/bot/src/commands/command.cpp
@@ -13,7 +13,6 @@
#include <string>
#include "../bundle.hpp"
-#include "../modules/notify.hpp"
#include "../modules/timer.hpp"
#include "../utils/chrono.hpp"
#include "commands/lua.hpp"
@@ -24,7 +23,6 @@
namespace bot {
namespace command {
CommandLoader::CommandLoader() {
- this->add_command(std::make_unique<mod::Notify>());
this->add_command(std::make_unique<mod::Timer>());
this->add_command(std::make_unique<mod::LuaExecution>());
diff --git a/bot/src/modules/notify.hpp b/bot/src/modules/notify.hpp
deleted file mode 100644
index 7bea6a6..0000000
--- a/bot/src/modules/notify.hpp
+++ /dev/null
@@ -1,131 +0,0 @@
-#pragma once
-
-#include <string>
-#include <vector>
-
-#include "../bundle.hpp"
-#include "../commands/command.hpp"
-#include "../commands/response_error.hpp"
-#include "../schemas/stream.hpp"
-
-namespace bot {
- namespace mod {
- class Notify : public command::Command {
- std::string get_name() const override { return "notify"; }
-
- std::vector<std::string> get_subcommand_ids() const override {
- return {"sub", "unsub"};
- }
-
- command::Response run(const InstanceBundle &bundle,
- const command::Request &request) const override {
- if (!request.subcommand_id.has_value()) {
- throw ResponseException<NOT_ENOUGH_ARGUMENTS>(
- request, bundle.localization, command::SUBCOMMAND);
- }
-
- const std::string &subcommand_id = request.subcommand_id.value();
-
- if (!request.message.has_value()) {
- throw ResponseException<ResponseError::NOT_ENOUGH_ARGUMENTS>(
- request, bundle.localization, command::CommandArgument::TARGET);
- }
-
- const std::string &message = request.message.value();
- std::vector<std::string> s = utils::string::split_text(message, ' ');
-
- std::string target;
- schemas::EventType type;
-
- std::vector<std::string> target_and_type =
- utils::string::split_text(s[0], ':');
-
- if (target_and_type.size() != 2) {
- throw ResponseException<ResponseError::INCORRECT_ARGUMENT>(
- request, bundle.localization, s[0]);
- }
-
- s.erase(s.begin());
-
- target = target_and_type[0];
- type = schemas::string_to_event_type(target_and_type[1]);
-
- std::string t = target_and_type[0] + ":" + target_and_type[1];
-
- auto channels = bundle.helix_client.get_users({target});
- api::twitch::schemas::User channel;
-
- if (channels.empty() && type < schemas::EventType::GITHUB) {
- throw ResponseException<ResponseError::NOT_FOUND>(
- request, bundle.localization, t);
- }
-
- pqxx::work work(request.conn);
- std::string query;
-
- if (type < schemas::GITHUB) {
- channel = channels[0];
-
- query = "SELECT id FROM events WHERE channel_id = " +
- std::to_string(request.channel.get_id()) +
- " AND target_alias_id = " + std::to_string(channel.id) +
- " AND event_type = " + std::to_string(type);
- } else {
- query = "SELECT id FROM events WHERE channel_id = " +
- std::to_string(request.channel.get_id()) +
- " AND custom_alias_id = '" + target +
- "' AND event_type = " + std::to_string(type);
- }
-
- pqxx::result events = work.exec(query);
-
- if (events.empty()) {
- throw ResponseException<ResponseError::NOT_FOUND>(
- request, bundle.localization, t);
- }
-
- pqxx::row event = events[0];
-
- pqxx::result subs =
- work.exec("SELECT id FROM event_subscriptions WHERE event_id = " +
- std::to_string(event[0].as<int>()) + " AND user_id = " +
- std::to_string(request.user.get_id()));
-
- if (subcommand_id == "sub") {
- if (!subs.empty()) {
- throw ResponseException<ResponseError::NAMESAKE_CREATION>(
- request, bundle.localization, t);
- }
-
- work.exec(
- "INSERT INTO event_subscriptions(event_id, user_id) VALUES (" +
- std::to_string(event[0].as<int>()) + ", " +
- std::to_string(request.user.get_id()) + ")");
- work.commit();
-
- return command::Response(
- bundle.localization
- .get_formatted_line(request, loc::LineId::NotifySub, {t})
- .value());
- } else if (subcommand_id == "unsub") {
- if (subs.empty()) {
- throw ResponseException<ResponseError::NOT_FOUND>(
- request, bundle.localization, t);
- }
-
- work.exec("DELETE FROM event_subscriptions WHERE id = " +
- std::to_string(subs[0][0].as<int>()));
- work.commit();
-
- return command::Response(
- bundle.localization
- .get_formatted_line(request, loc::LineId::NotifyUnsub, {t})
- .value());
- }
-
- throw ResponseException<ResponseError::SOMETHING_WENT_WRONG>(
- request, bundle.localization);
- }
- };
- }
-}
diff --git a/luamods/notify.lua b/luamods/notify.lua
new file mode 100644
index 0000000..afd4322
--- /dev/null
+++ b/luamods/notify.lua
@@ -0,0 +1,211 @@
+local function parse_target(value)
+ local parts = str_split(value, ':')
+ if #parts ~= 2 then
+ return nil
+ end
+
+ local data = {
+ target = parts[1],
+ type = str_to_event_type(parts[2])
+ }
+
+ if data.type == nil then
+ return data
+ end
+
+ if data.type < 10 then
+ local users = twitch_get_users({ logins = { data.target } })
+ if #users == 0 then
+ data.target = nil
+ return data
+ end
+
+ data.target = users[1]
+ end
+
+ return data
+end
+
+local lines = {
+ english = {
+ ["no_subcommand"] =
+ "{sender.alias_name}: No subcommand provided. Use {channel.prefix}help notify for more information.",
+ ["no_message"] = "{sender.alias_name}: No message provided.",
+ ["not_parseable"] = "{sender.alias_name}: This value cannot be parsed. (%s)",
+ ["unknown_type"] = "{sender.alias_name}: Unknown event type. (%s)",
+ ["user_not_found"] = "{sender.alias_name}: User not found. (%s)",
+ ["not_found"] = "{sender.alias_name}: Event %s not found.",
+ ["namesake"] = "{sender.alias_name}: You have already subscribed to this event.",
+ ["list"] =
+ "{sender.alias_name}: You can use '{channel.prefix}event list' to find out which events you can subscribe to.",
+ ["subs"] = "{sender.alias_name}: %s",
+ ["empty_subs"] = "{sender.alias_name}: You do not have any event subscriptions in this channel.",
+ ["sub"] =
+ "{sender.alias_name}: You have successfully subscribed to event %s",
+ ["unsub"] = "{sender.alias_name}: You have successfully unsubscribed from event %s"
+ },
+ russian = {
+ ["no_subcommand"] =
+ "{sender.alias_name}: Подкоманда не предоставлена. Используйте {channel.prefix}help event для большей информации.",
+ ["no_message"] = "{sender.alias_name}: Сообщение не предоставлено.",
+ ["not_parseable"] = "{sender.alias_name}: Это значение не может быть использовано. (%s)",
+ ["unknown_type"] = "{sender.alias_name}: Неизвестный тип события. (%s)",
+ ["user_not_found"] = "{sender.alias_name}: Пользователь не найден. (%s)",
+ ["not_found"] = "{sender.alias_name}: Событие %s не найдено.",
+ ["no_target"] = "{sender.alias_name}: Следующие значение события должно быть предоставлено.",
+ ["namesake"] = "{sender.alias_name}: Такое же событие уже существует.",
+ ["list"] =
+ "{sender.alias_name}: Вы можете использовать '{channel.prefix}event list', чтобы узнать на какие события Вы можете подписаться.",
+ ["subs"] = "{sender.alias_name}: %s",
+ ["empty_subs"] = "{sender.alias_name}: Вы не подписаны на какие-либо события в этом канале.",
+ ["sub"] =
+ "{sender.alias_name}: Вы успешно подписались на событие %s",
+ ["unsub"] = "{sender.alias_name}: Вы отписались от события %s",
+ },
+}
+
+return {
+ name = "notify",
+ description = [[
+The `!notify` command gives users the ability to manage event subscriptions.
+
+> Event must be created before using `!notify` command. See about `!event` command [here](/!event).
+
+## Syntax
+
+### Subscribe to the event
+`!notify sub [name]:[type]`
+
++ `[name]` - Twitch username or event name *(custom type only)*.
++ `[type]` - [Event type](/scripts.php?id=event#event-types).
+
+### Unsubscribe from the event
+`!notify unsub [name]:[type]`
+
++ `[name]` - Twitch username or event name *(custom type only)*.
++ `[type]` - [Event type](/scripts.php?id=event#event-types).
+
+### Get your event subscriptions
+`!notify subs`
+
+### Get available events to subscribe
+`!notify list`
+]],
+ delay_sec = 1,
+ options = {},
+ aliases = {},
+ subcommands = { "sub", "unsub", "subs", "list" },
+ minimal_rights = "user",
+ handle = function(request)
+ if request.subcommand_id == nil then
+ return l10n_custom_formatted_line_request(request, lines, "no_subcommand", {})
+ end
+
+ local scid = request.subcommand_id
+
+ if scid == "list" then
+ return l10n_custom_formatted_line_request(request, lines, "list", {})
+ elseif scid == "subs" then
+ local names = {}
+
+ local events = db_query([[
+SELECT e.name, e.event_type FROM events e
+INNER JOIN event_subscriptions es ON es.event_id = e.id
+WHERE e.channel_id = $1 AND es.user_id = $2
+]],
+ { request.channel.id, request.sender.id })
+ local user_ids = {}
+ for i = 1, #events, 1 do
+ local e = events[i]
+ local t = tonumber(e.event_type)
+ if t < 10 then
+ local id = tonumber(e.name)
+ table.insert(names, { name = id, type = t })
+ table.insert(user_ids, id)
+ print(id)
+ else
+ table.insert(names, { name = e.name, type = event_type_to_str(t) })
+ end
+ end
+ if #user_ids > 0 then
+ local users = twitch_get_users({ ids = user_ids })
+ for i = 1, #users, 1 do
+ local user = users[i]
+ for j = 1, #names, 1 do
+ if type(names[j].name) == "number" and
+ type(names[j].type) == "number" and
+ names[j].type < 10 and names[j].name == user.id
+ then
+ names[j].name = user.login
+ names[j].type = event_type_to_str(names[j].type)
+ end
+ end
+ end
+ end
+
+ -- finalizing
+ local n = {}
+ for i = 1, #names, 1 do
+ table.insert(n, names[i].name .. ':' .. names[i].type)
+ end
+
+ local line_id = "subs"
+ if #n == 0 then
+ line_id = "empty_subs"
+ end
+
+ return l10n_custom_formatted_line_request(request, lines, line_id, { table.concat(n, ', ') })
+ end
+
+ if request.message == nil then
+ return l10n_custom_formatted_line_request(request, lines, "no_message", {})
+ end
+
+ local parts = str_split(request.message, ' ')
+
+ local data_original = parts[1]
+ local data = parse_target(data_original)
+ table.remove(parts, 1)
+ local data_name = nil
+
+ if data == nil then
+ return l10n_custom_formatted_line_request(request, lines, "not_parseable", { data_original })
+ elseif data.type == nil then
+ return l10n_custom_formatted_line_request(request, lines, "unknown_type", { data_original })
+ elseif type(data.target) == "nil" then
+ return l10n_custom_formatted_line_request(request, lines, "user_not_found", { data_original })
+ elseif type(data.target) == "string" then
+ data_name = data.target
+ elseif type(data.target) == "table" then
+ data_name = data.target.id
+ end
+
+ local events = db_query([[
+SELECT e.id, es.id AS sub_id
+FROM events e
+LEFT JOIN event_subscriptions es ON es.id = e.id AND es.user_id = $3
+WHERE e.name = $1 AND e.event_type = $2
+]],
+ { data_name, data.type, request.sender.id })
+
+ if #events == 0 then
+ return l10n_custom_formatted_line_request(request, lines, "not_found", { data_original })
+ end
+
+ local event = events[1]
+
+ if scid == "sub" then
+ if event.sub_id ~= nil then
+ return l10n_custom_formatted_line_request(request, lines, "namesake", { data_original })
+ end
+
+ db_execute('INSERT INTO event_subscriptions(event_id, user_id) VALUES ($1, $2)',
+ { event.id, request.sender.id })
+
+ return l10n_custom_formatted_line_request(request, lines, "sub", { data_original })
+ elseif scid == "unsub" then
+ db_execute('DELETE FROM event_subscriptions WHERE id = $1', { event.sub_id })
+ return l10n_custom_formatted_line_request(request, lines, "unsub", { data_original })
+ end
+ end
+}