Skip to content

Commit 8044672

Browse files
committed
Add /tag random command
1 parent 0e35534 commit 8044672

File tree

7 files changed

+133
-6
lines changed

7 files changed

+133
-6
lines changed

lib/src/commands/tag.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,23 @@ final tag = ChatGroup(
6262
await Injector.appInstance.get<TagModule>().registerTagUsedEvent(TagUsedEvent.fromTag(tag: tag, hidden: false));
6363
}),
6464
),
65+
ChatCommand(
66+
'random',
67+
'Show a random tag',
68+
id('tag-random', (ChatContext context) async {
69+
final module = Injector.appInstance.get<TagModule>();
70+
final guildOrUser = context.guild?.id ?? context.user.id;
71+
72+
final randomTag = await module.getRandomTag(guildOrUser);
73+
74+
if (randomTag == null) {
75+
return await context.respond(MessageBuilder(content: 'Cannot fetch random tag...'));
76+
}
77+
module.registerTagUsedEvent(TagUsedEvent.fromTag(tag: randomTag, hidden: false));
78+
79+
return context.respond(MessageBuilder(content: randomTag.content));
80+
}),
81+
),
6582
ChatCommand(
6683
'preview',
6784
'View a tag, without making it publicly visible',

lib/src/modules/tag.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ class TagModule {
6262
return results.firstOrNull;
6363
}
6464

65+
Future<Tag?> getRandomTag(Snowflake guildId) async {
66+
return _tagRepository.fetchRandomActiveTag(guildId: guildId.toString());
67+
}
68+
6569
Future<Iterable<(TagUsedEvent, Tag)>> getTagUsage(Snowflake guildId, [Tag? tag]) {
6670
return _tagRepository.fetchTagUsage(guildId: guildId.toString(), tagId: tag?.id);
6771
}

lib/src/repository/tag.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,23 @@ class TagRepository {
5050
return result.first.toColumnMap()['count_tags'] ?? 0;
5151
}
5252

53+
Future<Tag?> fetchRandomActiveTag({required String guildId}) async {
54+
final query = SelectQuery.selectAll('tags')
55+
..andWhere('enabled = TRUE')
56+
..andWhere('guild_id = @guildId')
57+
..orderBy('random()')
58+
..limit(1);
59+
60+
final params = <String, dynamic>{"guildId": guildId};
61+
62+
final result = await _database.executeQuery(query, parameters: params);
63+
if (result.isEmpty) {
64+
return null;
65+
}
66+
67+
return Tag.fromRow(result.first.toColumnMap());
68+
}
69+
5370
Future<Iterable<Tag>> searchActiveTags({
5471
required String query,
5572
int limit = 25,

lib/src/util/query_builder.dart

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,12 +215,18 @@ class DeleteQuery extends Query with _WhereQuery {
215215

216216
class SelectQuery extends Query with _WhereQuery, _JoinQuery {
217217
final List<String> _selects = [];
218+
final List<String> _orderBys = [];
219+
int? _limit;
220+
int? _offset;
218221

219222
SelectQuery(super.from, {super.alias});
220223
factory SelectQuery.selectAll(String from, {String? alias}) =>
221224
SelectQuery(from, alias: alias)..select("${alias != null ? '$alias.' : ''}*");
222225

223226
void select(String expression) => _selects.add(expression);
227+
void orderBy(String expression) => _orderBys.add(expression);
228+
void limit(int n) => _limit = n;
229+
void offset(int n) => _offset = n;
224230

225231
@override
226232
Sql build() {
@@ -232,8 +238,17 @@ class SelectQuery extends Query with _WhereQuery, _JoinQuery {
232238
buffer.write(" ");
233239
_buildWheres(buffer);
234240

235-
buffer.write(";");
241+
if (_orderBys.isNotEmpty) {
242+
buffer.write(" ORDER BY ${_orderBys.join(",")}");
243+
}
244+
if (_limit != null) {
245+
buffer.write(" LIMIT $_limit");
246+
}
247+
if (_offset != null) {
248+
buffer.write(" OFFSET $_offset");
249+
}
236250

251+
buffer.write(";");
237252
return Sql.named(buffer.toStringClean());
238253
}
239254
}

test/pipelines_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ void main() {
1616
expect(embed.author?.name, 'Pipeline Build');
1717
});
1818
});
19-
}
19+
}

test/query_builder_test.dart

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,83 @@ void main() {
148148
final query = SelectQuery("test");
149149
expect(() => query.build().asString(), throwsA(isA<QueryBuilderException>()));
150150
});
151+
test("Select with ORDER BY random() and LIMIT/OFFSET", () {
152+
final query = SelectQuery("test")
153+
..select("*")
154+
..andWhere("a = 1")
155+
..orderBy("random()")
156+
..limit(5)
157+
..offset(10);
158+
159+
expect(query.build().asString(), "SELECT * FROM test WHERE a = 1 ORDER BY random() LIMIT 5 OFFSET 10;");
160+
});
161+
162+
test("Select with multiple ORDER BY clauses", () {
163+
final query = SelectQuery("t")
164+
..select("*")
165+
..orderBy("a ASC")
166+
..orderBy("b DESC");
167+
168+
expect(query.build().asString(), "SELECT * FROM t ORDER BY a ASC,b DESC;");
169+
});
170+
171+
test("Select with LIMIT only", () {
172+
final query = SelectQuery("t")
173+
..select("*")
174+
..limit(3);
175+
176+
expect(query.build().asString(), "SELECT * FROM t LIMIT 3;");
177+
});
178+
179+
test("Select with OFFSET only", () {
180+
final query = SelectQuery("t")
181+
..select("*")
182+
..offset(7);
183+
184+
expect(query.build().asString(), "SELECT * FROM t OFFSET 7;");
185+
});
186+
187+
test("Select with JOIN + WHERE + ORDER BY + LIMIT", () {
188+
final query = SelectQuery("tag_usage", alias: "tu")
189+
..select("tu.*")
190+
..addJoin("tags", "t", ["t.id = tu.command_id"])
191+
..andWhere("t.enabled = TRUE")
192+
..orderBy("t.id DESC")
193+
..limit(1);
194+
195+
expect(
196+
query.build().asString(),
197+
"SELECT tu.* FROM tag_usage tu JOIN tags t ON t.id = tu.command_id WHERE t.enabled = TRUE ORDER BY t.id DESC LIMIT 1;",
198+
);
199+
});
200+
test("Select alias selectAll with ORDER BY + LIMIT + OFFSET", () {
201+
final query = SelectQuery.selectAll("test", alias: "t")
202+
..orderBy("t.a DESC")
203+
..limit(2)
204+
..offset(1);
205+
206+
expect(query.build().asString(), "SELECT t.* FROM test t ORDER BY t.a DESC LIMIT 2 OFFSET 1;");
207+
});
208+
209+
test("Select LIMIT/OFFSET render order is LIMIT then OFFSET regardless of call order", () {
210+
final query = SelectQuery("t")
211+
..select("*")
212+
..offset(100)
213+
..limit(10);
214+
215+
expect(query.build().asString(), "SELECT * FROM t LIMIT 10 OFFSET 100;");
216+
});
217+
218+
test("Select with mixed AND/OR WHEREs and ORDER BY", () {
219+
final query = SelectQuery("t")
220+
..select("*")
221+
..andWhere("a = 1")
222+
..andWhere("b = 2")
223+
..orWhere("c = 3")
224+
..orderBy("id");
225+
226+
expect(query.build().asString(), "SELECT * FROM t WHERE a = 1 AND b = 2 OR c = 3 ORDER BY id;");
227+
});
151228
});
152229

153230
group("Update tests", () {

test/utils_test.dart

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,7 @@ void main() {
174174
});
175175

176176
test('getDurationFromStringOrDefault invalid string returns default', () {
177-
expect(
178-
getDurationFromStringOrDefault('not a duration', const Duration(seconds: 5)),
179-
const Duration(seconds: 5),
180-
);
177+
expect(getDurationFromStringOrDefault('not a duration', const Duration(seconds: 5)), const Duration(seconds: 5));
181178
});
182179

183180
test('valueOrNull returns original non-empty string (not trimmed)', () {

0 commit comments

Comments
 (0)