From f4f79a3d5f228e8c5002879301e726f09609b881 Mon Sep 17 00:00:00 2001
From: jcorporation <mail@jcgames.de>
Date: Sat, 21 Oct 2023 18:08:14 +0200
Subject: [PATCH] New command "stickernames" lists uniq and sorted sticker
 names

---
 doc/protocol.rst                |  3 +++
 src/command/AllCommands.cxx     |  1 +
 src/command/StickerCommands.cxx | 36 +++++++++++++++++++++++++++++++++
 src/command/StickerCommands.hxx |  2 ++
 src/sticker/Database.cxx        | 21 +++++++++++++++++++
 src/sticker/Database.hxx        |  6 ++++++
 6 files changed, 69 insertions(+)

diff --git a/doc/protocol.rst b/doc/protocol.rst
index 5b5e2cf03..214ab7520 100644
--- a/doc/protocol.rst
+++ b/doc/protocol.rst
@@ -1513,6 +1513,9 @@ Examples:
     sticker: name_1=value_1
     OK
 
+:command:`stickernames`
+    Gets a list of uniq sticker names.
+
 Connection settings
 ===================
 
diff --git a/src/command/AllCommands.cxx b/src/command/AllCommands.cxx
index da0ebd2b7..ddfe9a2b9 100644
--- a/src/command/AllCommands.cxx
+++ b/src/command/AllCommands.cxx
@@ -186,6 +186,7 @@ static constexpr struct command commands[] = {
 	{ "status", PERMISSION_READ, 0, 0, handle_status },
 #ifdef ENABLE_SQLITE
 	{ "sticker", PERMISSION_ADMIN, 3, -1, handle_sticker },
+	{ "stickernames", PERMISSION_ADMIN, 0, 0, handle_sticker_names },
 #endif
 	{ "stop", PERMISSION_PLAYER, 0, 0, handle_stop },
 	{ "subscribe", PERMISSION_READ, 1, 1, handle_subscribe },
diff --git a/src/command/StickerCommands.cxx b/src/command/StickerCommands.cxx
index 800114efa..4f74049a0 100644
--- a/src/command/StickerCommands.cxx
+++ b/src/command/StickerCommands.cxx
@@ -102,6 +102,24 @@ public:
 		return CommandResult::OK;
 	}
 
+	virtual CommandResult Names() {
+		auto data = CallbackContext{
+			.name = "",
+			.sticker_type = sticker_type,
+			.response = response,
+			.is_song = StringIsEqual("song", sticker_type)
+		};
+
+		auto callback = [](const char *found_value, void *user_data) {
+			auto context = reinterpret_cast<CallbackContext *>(user_data);
+			context->response.Fmt("name: {}\n", found_value);
+		};
+
+		sticker_database.Names(callback, &data);
+
+		return CommandResult::OK;
+	}
+
 protected:
 	DomainHandler(Response &_response,
 		      const Database &_database,
@@ -292,6 +310,24 @@ private:
 
 } // namespace
 
+CommandResult
+handle_sticker_names(Client &client, Request args, Response &r)
+{
+	(void) args;
+	auto &instance = client.GetInstance();
+	if (!instance.HasStickerDatabase()) {
+		r.Error(ACK_ERROR_UNKNOWN, "sticker database is disabled");
+		return CommandResult::ERROR;
+	}
+
+	auto &db = client.GetPartition().GetDatabaseOrThrow();
+	auto &sticker_database = *instance.sticker_database;
+
+	std::unique_ptr<DomainHandler> handler = std::make_unique<SongHandler>(r, db, sticker_database);
+
+	return handler->Names();
+}
+
 CommandResult
 handle_sticker(Client &client, Request args, Response &r)
 {
diff --git a/src/command/StickerCommands.hxx b/src/command/StickerCommands.hxx
index 08912f089..aa3aeef2c 100644
--- a/src/command/StickerCommands.hxx
+++ b/src/command/StickerCommands.hxx
@@ -12,5 +12,7 @@ class Response;
 
 CommandResult
 handle_sticker(Client &client, Request request, Response &response);
+CommandResult
+handle_sticker_names(Client &client, Request request, Response &response);
 
 #endif
diff --git a/src/sticker/Database.cxx b/src/sticker/Database.cxx
index 2c78fe997..c5b3429e2 100644
--- a/src/sticker/Database.cxx
+++ b/src/sticker/Database.cxx
@@ -32,6 +32,7 @@ enum sticker_sql {
 	STICKER_SQL_TRANSACTION_BEGIN,
 	STICKER_SQL_TRANSACTION_COMMIT,
 	STICKER_SQL_TRANSACTION_ROLLBACK,
+	STICKER_SQL_NAMES,
 
 	STICKER_SQL_COUNT
 };
@@ -72,6 +73,9 @@ static constexpr auto sticker_sql = std::array {
 
 	//[STICKER_SQL_TRANSACTION_ROLLBACK]
 	"ROLLBACK",
+
+	//[STICKER_SQL_NAMES]
+	"SELECT DISTINCT name FROM sticker order by name",
 };
 
 static constexpr const char sticker_sql_create[] =
@@ -366,6 +370,23 @@ StickerDatabase::GetUniqueStickers()
 	return result;
 }
 
+void
+StickerDatabase::Names(void (*func)(const char *value, void *user_data), void *user_data)
+{
+	assert(func != nullptr);
+
+	sqlite3_stmt *const s = stmt[STICKER_SQL_NAMES];
+	assert(s != nullptr);
+
+	AtScopeExit(s) {
+		sqlite3_reset(s);
+	};
+
+	ExecuteForEach(s, [s, func, user_data](){
+			func((const char*)sqlite3_column_text(s, 0), user_data);
+		});
+}
+
 void
 StickerDatabase::BatchDeleteNoIdle(const std::list<StickerTypeUriPair> &stickers)
 {
diff --git a/src/sticker/Database.hxx b/src/sticker/Database.hxx
index c4a3cceef..8d7f8d28f 100644
--- a/src/sticker/Database.hxx
+++ b/src/sticker/Database.hxx
@@ -54,6 +54,7 @@ class StickerDatabase {
 		  SQL_TRANSACTION_BEGIN,
 		  SQL_TRANSACTION_COMMIT,
 		  SQL_TRANSACTION_ROLLBACK,
+		  SQL_NAMES,
 
 		  SQL_COUNT
 	};
@@ -146,6 +147,11 @@ public:
 			       void *user_data),
 		  void *user_data);
 
+	/**
+	 * Uniq and sorted list of all sticker names
+	 */
+	void Names(void (*func)(const char *value, void *user_data), void *user_data);
+
 	using StickerTypeUriPair = std::pair<std::string, std::string>;
 
 	/**