cordscript 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. cordscript/__init__.py +6 -0
  2. cordscript/client.py +590 -0
  3. cordscript/core/__init__.py +9 -0
  4. cordscript/core/context.py +33 -0
  5. cordscript/core/runtime.py +55 -0
  6. cordscript/core/variables.py +16 -0
  7. cordscript/exceptions/__init__.py +3 -0
  8. cordscript/exceptions/errors.py +9 -0
  9. cordscript/functions/__init__.py +45 -0
  10. cordscript/functions/database/__init__.py +4 -0
  11. cordscript/functions/database/database.py +846 -0
  12. cordscript/functions/database/store.py +69 -0
  13. cordscript/functions/discord/__init__.py +15 -0
  14. cordscript/functions/discord/components.py +134 -0
  15. cordscript/functions/discord/componentsv2.py +92 -0
  16. cordscript/functions/discord/embed.py +63 -0
  17. cordscript/functions/discord/interaction.py +48 -0
  18. cordscript/functions/discord/logic.py +87 -0
  19. cordscript/functions/discord/message.py +51 -0
  20. cordscript/functions/discord/moderation.py +179 -0
  21. cordscript/functions/discord/status.py +56 -0
  22. cordscript/functions/users/__init__.py +3 -0
  23. cordscript/functions/users/user.py +103 -0
  24. cordscript/functions/utility/__init__.py +14 -0
  25. cordscript/functions/utility/cooldown.py +35 -0
  26. cordscript/functions/utility/http.py +73 -0
  27. cordscript/functions/utility/json.py +201 -0
  28. cordscript/functions/utility/math.py +102 -0
  29. cordscript/functions/utility/random.py +27 -0
  30. cordscript/functions/utility/strings.py +201 -0
  31. cordscript/functions/utility/time.py +41 -0
  32. cordscript/parser.py +190 -0
  33. cordscript/setup.py +38 -0
  34. cordscript-1.0.0.dist-info/METADATA +17 -0
  35. cordscript-1.0.0.dist-info/RECORD +37 -0
  36. cordscript-1.0.0.dist-info/WHEEL +5 -0
  37. cordscript-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,846 @@
1
+ from __future__ import annotations
2
+ from .store import Database
3
+ import sys
4
+ import os
5
+ from contextlib import redirect_stdout, redirect_stderr
6
+
7
+ class SuppressOutput:
8
+ def __enter__(self):
9
+ self._stdout = sys.stdout
10
+ self._stderr = sys.stderr
11
+ sys.stdout = open(os.devnull, 'w')
12
+ sys.stderr = open(os.devnull, 'w')
13
+ return self
14
+
15
+ def __exit__(self, *args):
16
+ sys.stdout.close()
17
+ sys.stderr.close()
18
+ sys.stdout = self._stdout
19
+ sys.stderr = self._stderr
20
+
21
+ def _detect_db(db) -> str:
22
+ t = type(db).__name__.lower()
23
+ module = type(db).__module__.lower()
24
+ if "motor" in module or "asynciomotorclient" in t:
25
+ return "motor"
26
+ if "mongoclient" in t or "mongo" in module:
27
+ return "pymongo"
28
+ if "asyncpg" in module:
29
+ return "asyncpg"
30
+ if "psycopg" in module:
31
+ return "psycopg"
32
+ if "bettersqlite" in t or "better_sqlite3" in module:
33
+ return "better_sqlite3"
34
+ if "connection" in t and "sqlite" in module:
35
+ return "sqlite3"
36
+ return "unknown"
37
+
38
+ async def _init_table(db):
39
+ kind = _detect_db(db)
40
+ sql = """
41
+ CREATE TABLE IF NOT EXISTS cordscript_vars (
42
+ user_id TEXT,
43
+ key TEXT,
44
+ value TEXT,
45
+ PRIMARY KEY (user_id, key)
46
+ )
47
+ """
48
+ if kind == "motor":
49
+ await db["cordscript_vars"].create_index([("user_id", 1), ("key", 1)], unique=True)
50
+ elif kind in ("asyncpg", "psycopg"):
51
+ await db.execute(sql)
52
+ elif kind == "sqlite3":
53
+ cur = db.cursor()
54
+ cur.execute(sql)
55
+ db.commit()
56
+ elif kind == "better_sqlite3":
57
+ db.prepare(sql).run()
58
+
59
+ async def connect_from_url(url: str):
60
+ if url.startswith("mongodb"):
61
+ try:
62
+ import motor.motor_asyncio
63
+ client = motor.motor_asyncio.AsyncIOMotorClient(url)
64
+ return client.get_default_database() or client["cordscript"]
65
+ except ImportError:
66
+ raise ImportError("motor not installed: pip install motor")
67
+ if url.startswith("postgresql") or url.startswith("postgres"):
68
+ try:
69
+ import asyncpg
70
+ return await asyncpg.connect(url)
71
+ except ImportError:
72
+ raise ImportError("asyncpg not installed: pip install asyncpg")
73
+ if url.startswith("sqlite"):
74
+ path = url.replace("sqlite:///", "").replace("sqlite://", "")
75
+ import sqlite3
76
+ return sqlite3.connect(path)
77
+ raise ValueError(f"Unrecognized database URL: {url}")
78
+
79
+ async def _get_legacy(db, key: str, user_id: str) -> str:
80
+ kind = _detect_db(db)
81
+ try:
82
+ if kind == "motor":
83
+ doc = await db["cordscript_vars"].find_one({"user_id": user_id, "key": key})
84
+ return str(doc["value"]) if doc else ""
85
+ if kind == "pymongo":
86
+ doc = db["cordscript_vars"].find_one({"user_id": user_id, "key": key})
87
+ return str(doc["value"]) if doc else ""
88
+ if kind in ("asyncpg", "psycopg"):
89
+ row = await db.fetchrow("SELECT value FROM cordscript_vars WHERE user_id=$1 AND key=$2", user_id, key)
90
+ return str(row["value"]) if row else ""
91
+ if kind == "sqlite3":
92
+ cur = db.cursor()
93
+ cur.execute("SELECT value FROM cordscript_vars WHERE user_id=? AND key=?", (user_id, key))
94
+ row = cur.fetchone()
95
+ return str(row[0]) if row else ""
96
+ if kind == "better_sqlite3":
97
+ row = db.prepare("SELECT value FROM cordscript_vars WHERE user_id=? AND key=?").get(user_id, key)
98
+ return str(row["value"]) if row else ""
99
+ except Exception as e:
100
+ return f"[Error in $getVar: {e}]"
101
+ return ""
102
+
103
+ async def _set_legacy(db, key: str, value: str, user_id: str):
104
+ kind = _detect_db(db)
105
+ try:
106
+ if kind == "motor":
107
+ await db["cordscript_vars"].update_one(
108
+ {"user_id": user_id, "key": key}, {"$set": {"value": value}}, upsert=True
109
+ )
110
+ elif kind == "pymongo":
111
+ db["cordscript_vars"].update_one(
112
+ {"user_id": user_id, "key": key}, {"$set": {"value": value}}, upsert=True
113
+ )
114
+ elif kind in ("asyncpg", "psycopg"):
115
+ await db.execute("""
116
+ INSERT INTO cordscript_vars (user_id, key, value)
117
+ VALUES ($1, $2, $3)
118
+ ON CONFLICT (user_id, key) DO UPDATE SET value=$3
119
+ """, user_id, key, value)
120
+ elif kind == "sqlite3":
121
+ cur = db.cursor()
122
+ cur.execute("""
123
+ INSERT INTO cordscript_vars (user_id, key, value)
124
+ VALUES (?, ?, ?)
125
+ ON CONFLICT(user_id, key) DO UPDATE SET value=excluded.value
126
+ """, (user_id, key, value))
127
+ db.commit()
128
+ elif kind == "better_sqlite3":
129
+ db.prepare("""
130
+ INSERT INTO cordscript_vars (user_id, key, value)
131
+ VALUES (?, ?, ?)
132
+ ON CONFLICT(user_id, key) DO UPDATE SET value=excluded.value
133
+ """).run(user_id, key, value)
134
+ except Exception as e:
135
+ return f"[Error in $setVar: {e}]"
136
+
137
+ def _default_ids(ctx):
138
+ import discord
139
+ user_id = str(ctx.user.id) if isinstance(ctx, discord.Interaction) else str(getattr(ctx, "author", ctx).id)
140
+ channel = getattr(ctx, "channel", None)
141
+ channel_id = str(channel.id) if channel else ""
142
+ guild = getattr(ctx, "guild", None)
143
+ guild_id = str(guild.id) if guild else ""
144
+ return user_id, channel_id, guild_id
145
+
146
+ async def handle_database(fn: str, args: list[str], raw: str, parser) -> str | None:
147
+ db = getattr(parser.client, "_database", None)
148
+ if db is None:
149
+ return None
150
+
151
+ ctx = parser.ctx
152
+ default_uid, default_cid, default_gid = _default_ids(ctx)
153
+
154
+ def _val(index: int) -> str:
155
+ return args[index] if len(args) > index else ""
156
+
157
+ def _key(index: int = 0) -> str:
158
+ return _val(index).strip()
159
+
160
+ try:
161
+ if isinstance(db, Database):
162
+ match fn:
163
+ case "getuservar":
164
+ key = _key(0)
165
+ uid = _val(1).strip() or default_uid
166
+ if not key:
167
+ return "[Error in $getUserVar: variable name required]"
168
+ return db.get("user", uid, key)
169
+
170
+ case "setuservar":
171
+ key = _key(0)
172
+ value = _val(1)
173
+ uid = _val(2).strip() or default_uid
174
+ if not key:
175
+ return "[Error in $setUserVar: variable name required]"
176
+ db.set("user", uid, key, value)
177
+ return ""
178
+
179
+ case "getchannelvar":
180
+ key = _key(0)
181
+ cid = _val(1).strip() or default_cid
182
+ if not key:
183
+ return "[Error in $getChannelVar: variable name required]"
184
+ return db.get("channel", cid, key)
185
+
186
+ case "setchannelvar":
187
+ key = _key(0)
188
+ value = _val(1)
189
+ cid = _val(2).strip() or default_cid
190
+ if not key:
191
+ return "[Error in $setChannelVar: variable name required]"
192
+ db.set("channel", cid, key, value)
193
+ return ""
194
+
195
+ case "getguildvar":
196
+ key = _key(0)
197
+ gid = _val(1).strip() or default_gid
198
+ if not key:
199
+ return "[Error in $getGuildVar: variable name required]"
200
+ return db.get("guild", gid, key)
201
+
202
+ case "setguildvar":
203
+ key = _key(0)
204
+ value = _val(1)
205
+ gid = _val(2).strip() or default_gid
206
+ if not key:
207
+ return "[Error in $setGuildVar: variable name required]"
208
+ db.set("guild", gid, key, value)
209
+ return ""
210
+
211
+ case "getvar":
212
+ key = _key(0)
213
+ uid = _val(1).strip() or default_uid
214
+ if not key:
215
+ return "[Error in $getVar: variable name required]"
216
+ return db.get("user", uid, key)
217
+
218
+ case "setvar":
219
+ key = _key(0)
220
+ value = _val(1)
221
+ uid = _val(2).strip() or default_uid
222
+ if not key:
223
+ return "[Error in $setVar: variable name required]"
224
+ db.set("user", uid, key, value)
225
+ return ""
226
+
227
+ case "getdb":
228
+ import json
229
+ try:
230
+ with SuppressOutput():
231
+ all_data = db.get_all() if hasattr(db, 'get_all') else {}
232
+ return json.dumps(all_data, indent=2, ensure_ascii=False)
233
+ except Exception as e:
234
+ return f"[Error in $getDB: {e}]"
235
+
236
+ case "searchdb":
237
+ import json
238
+ try:
239
+ path = _key(0)
240
+ with SuppressOutput():
241
+ all_data = db.get_all() if hasattr(db, 'get_all') else {}
242
+
243
+ if not path:
244
+ return json.dumps(all_data, indent=2, ensure_ascii=False)
245
+
246
+ results = {}
247
+ def search_recursive(obj, search_term, current_path=""):
248
+ if isinstance(obj, dict):
249
+ for key, value in obj.items():
250
+ new_path = f"{current_path}.{key}" if current_path else key
251
+ if search_term.lower() in key.lower():
252
+ results[new_path] = value
253
+ search_recursive(value, search_term, new_path)
254
+ elif isinstance(obj, list):
255
+ for i, item in enumerate(obj):
256
+ new_path = f"{current_path}[{i}]"
257
+ search_recursive(item, search_term, new_path)
258
+ elif isinstance(obj, str) and search_term.lower() in obj.lower():
259
+ results[current_path] = obj
260
+
261
+ search_recursive(all_data, path)
262
+
263
+ if results:
264
+ return json.dumps(results, indent=2, ensure_ascii=False)
265
+ else:
266
+ return f"[No results found for '{path}']"
267
+ except Exception as e:
268
+ return f"[Error in $searchDB: {e}]"
269
+
270
+ case "wipedb":
271
+ try:
272
+ with SuppressOutput():
273
+ if hasattr(db, 'wipe'):
274
+ db.wipe()
275
+ return "[Database wiped successfully]"
276
+ else:
277
+ return "[Error in $wipeDB: wipe method not available]"
278
+ except Exception as e:
279
+ return f"[Error in $wipeDB: {e}]"
280
+
281
+ case "getuserleaderboardid":
282
+ name = _key(0)
283
+ sort_type = _key(1) or "DESC"
284
+ position = int(_key(2)) if _key(2) else 1
285
+
286
+ if not name:
287
+ return "[Error in $getUserLeaderboardID: name required]"
288
+
289
+ try:
290
+ with SuppressOutput():
291
+ all_data = db.get_all() if hasattr(db, 'get_all') else {}
292
+
293
+ leaderboard = {}
294
+ if "user" in all_data:
295
+ for user_id, vars_dict in all_data.get("user", {}).items():
296
+ if name in vars_dict:
297
+ try:
298
+ value = float(vars_dict[name])
299
+ leaderboard[user_id] = value
300
+ except (ValueError, TypeError):
301
+ pass
302
+
303
+ if sort_type.upper() == "ASC":
304
+ sorted_lb = sorted(leaderboard.items(), key=lambda x: x[1])
305
+ else:
306
+ sorted_lb = sorted(leaderboard.items(), key=lambda x: x[1], reverse=True)
307
+
308
+ if 1 <= position <= len(sorted_lb):
309
+ return sorted_lb[position - 1][0]
310
+ else:
311
+ return "[Position not found]"
312
+ except Exception as e:
313
+ return f"[Error in $getUserLeaderboardID: {e}]"
314
+
315
+ case "userleaderboard":
316
+ name = _key(0)
317
+ sort_type = _key(1) or "DESC"
318
+ max_items = int(_key(2)) if _key(2) else 10
319
+ page = int(_key(3)) if _key(3) else 1
320
+ separator = _val(4) or "\n"
321
+ env_value = _key(5) or "{value}"
322
+ env_position = _key(6) or "{position}"
323
+ code = _val(7) or "{position}. {user_id} - {value}"
324
+
325
+ if not name:
326
+ return "[Error in $userLeaderboard: name required]"
327
+
328
+ try:
329
+ with SuppressOutput():
330
+ all_data = db.get_all() if hasattr(db, 'get_all') else {}
331
+
332
+ leaderboard = {}
333
+ if "user" in all_data:
334
+ for user_id, vars_dict in all_data.get("user", {}).items():
335
+ if name in vars_dict:
336
+ try:
337
+ value = float(vars_dict[name])
338
+ leaderboard[user_id] = value
339
+ except (ValueError, TypeError):
340
+ pass
341
+
342
+ if sort_type.upper() == "ASC":
343
+ sorted_lb = sorted(leaderboard.items(), key=lambda x: x[1])
344
+ else:
345
+ sorted_lb = sorted(leaderboard.items(), key=lambda x: x[1], reverse=True)
346
+
347
+ start = (page - 1) * max_items
348
+ end = start + max_items
349
+ page_data = sorted_lb[start:end]
350
+
351
+ if not page_data:
352
+ return "[No data for this page]"
353
+
354
+ result = []
355
+ for pos, (user_id, value) in enumerate(page_data, start=start + 1):
356
+ line = code.replace(env_value, str(value))
357
+ line = line.replace(env_position, str(pos))
358
+ line = line.replace("{user_id}", user_id)
359
+ line = line.replace("{value}", str(value))
360
+ line = line.replace("{position}", str(pos))
361
+ result.append(line)
362
+
363
+ return separator.join(result)
364
+ except Exception as e:
365
+ return f"[Error in $userLeaderboard: {e}]"
366
+
367
+ case "getuserleaderboardlength":
368
+ name = _key(0)
369
+ length_type = _key(1) or "count"
370
+ decimals = int(_key(2)) if _key(2) else 0
371
+
372
+ if not name:
373
+ return "[Error in $getUserLeaderboardLength: name required]"
374
+
375
+ try:
376
+ with SuppressOutput():
377
+ all_data = db.get_all() if hasattr(db, 'get_all') else {}
378
+
379
+ values = []
380
+ if "user" in all_data:
381
+ for user_id, vars_dict in all_data.get("user", {}).items():
382
+ if name in vars_dict:
383
+ try:
384
+ value = float(vars_dict[name])
385
+ values.append(value)
386
+ except (ValueError, TypeError):
387
+ pass
388
+
389
+ if length_type.lower() == "count":
390
+ result = len(values)
391
+ elif length_type.lower() == "sum":
392
+ result = sum(values) if values else 0
393
+ elif length_type.lower() == "avg":
394
+ result = sum(values) / len(values) if values else 0
395
+ elif length_type.lower() == "max":
396
+ result = max(values) if values else 0
397
+ elif length_type.lower() == "min":
398
+ result = min(values) if values else 0
399
+ else:
400
+ result = len(values)
401
+
402
+ if decimals > 0:
403
+ result = round(result, decimals)
404
+ else:
405
+ result = int(result)
406
+
407
+ return str(result)
408
+ except Exception as e:
409
+ return f"[Error in $getUserLeaderboardLength: {e}]"
410
+
411
+ case "getuserleaderboardvalue":
412
+ name = _key(0)
413
+ sort_type = _key(1) or "DESC"
414
+ user_id = _key(2)
415
+
416
+ if not name:
417
+ return "[Error in $getUserLeaderboardValue: name required]"
418
+
419
+ if not user_id:
420
+ import discord
421
+ ctx = parser.ctx
422
+ user_id = str(ctx.user.id) if isinstance(ctx, discord.Interaction) else str(getattr(ctx, "author", ctx).id)
423
+
424
+ try:
425
+ with SuppressOutput():
426
+ all_data = db.get_all() if hasattr(db, 'get_all') else {}
427
+
428
+ leaderboard = {}
429
+ if "user" in all_data:
430
+ for uid, vars_dict in all_data.get("user", {}).items():
431
+ if name in vars_dict:
432
+ try:
433
+ value = float(vars_dict[name])
434
+ leaderboard[uid] = value
435
+ except (ValueError, TypeError):
436
+ pass
437
+
438
+ if str(user_id) not in leaderboard:
439
+ return "[User not in leaderboard]"
440
+
441
+ user_value = leaderboard[str(user_id)]
442
+
443
+ if sort_type.upper() == "ASC":
444
+ sorted_lb = sorted(leaderboard.items(), key=lambda x: x[1])
445
+ else:
446
+ sorted_lb = sorted(leaderboard.items(), key=lambda x: x[1], reverse=True)
447
+
448
+ position = next((i + 1 for i, (uid, _) in enumerate(sorted_lb) if uid == str(user_id)), None)
449
+
450
+ return f"{user_value}|{position}"
451
+ except Exception as e:
452
+ return f"[Error in $getUserLeaderboardValue: {e}]"
453
+
454
+ case _:
455
+ return None
456
+
457
+ match fn:
458
+ case "setvar":
459
+ key = _key(0)
460
+ value = _val(1)
461
+ uid = _val(2).strip() or default_uid
462
+ if not key:
463
+ return "[Error in $setVar: variable name required]"
464
+ result = await _set_legacy(db, key, value, uid)
465
+ return result or ""
466
+
467
+ case "getvar":
468
+ key = _key(0)
469
+ uid = _val(1).strip() or default_uid
470
+ if not key:
471
+ return "[Error in $getVar: variable name required]"
472
+ return await _get_legacy(db, key, uid)
473
+
474
+ case "getdb":
475
+ import json
476
+ try:
477
+ kind = _detect_db(db)
478
+ all_data = {}
479
+
480
+ with SuppressOutput():
481
+ if kind == "motor":
482
+ cursor = db["cordscript_vars"].find({})
483
+ async for doc in cursor:
484
+ doc.pop("_id", None)
485
+ all_data[f"{doc['user_id']}_{doc['key']}"] = doc['value']
486
+ elif kind == "pymongo":
487
+ for doc in db["cordscript_vars"].find({}):
488
+ doc.pop("_id", None)
489
+ all_data[f"{doc['user_id']}_{doc['key']}"] = doc['value']
490
+ elif kind in ("asyncpg", "psycopg"):
491
+ rows = await db.fetch("SELECT * FROM cordscript_vars")
492
+ for row in rows:
493
+ all_data[f"{row['user_id']}_{row['key']}"] = row['value']
494
+ elif kind == "sqlite3":
495
+ cur = db.cursor()
496
+ cur.execute("SELECT * FROM cordscript_vars")
497
+ for row in cur.fetchall():
498
+ all_data[f"{row[0]}_{row[1]}"] = row[2]
499
+ elif kind == "better_sqlite3":
500
+ rows = db.prepare("SELECT * FROM cordscript_vars").all()
501
+ for row in rows:
502
+ all_data[f"{row['user_id']}_{row['key']}"] = row['value']
503
+
504
+ return json.dumps(all_data, indent=2, ensure_ascii=False)
505
+ except Exception as e:
506
+ return f"[Error in $getDB: {e}]"
507
+
508
+ case "searchdb":
509
+ import json
510
+ try:
511
+ path = _key(0)
512
+ kind = _detect_db(db)
513
+ all_data = {}
514
+
515
+ with SuppressOutput():
516
+ if kind == "motor":
517
+ cursor = db["cordscript_vars"].find({})
518
+ async for doc in cursor:
519
+ doc.pop("_id", None)
520
+ all_data[f"{doc['user_id']}_{doc['key']}"] = doc['value']
521
+ elif kind == "pymongo":
522
+ for doc in db["cordscript_vars"].find({}):
523
+ doc.pop("_id", None)
524
+ all_data[f"{doc['user_id']}_{doc['key']}"] = doc['value']
525
+ elif kind in ("asyncpg", "psycopg"):
526
+ rows = await db.fetch("SELECT * FROM cordscript_vars")
527
+ for row in rows:
528
+ all_data[f"{row['user_id']}_{row['key']}"] = row['value']
529
+ elif kind == "sqlite3":
530
+ cur = db.cursor()
531
+ cur.execute("SELECT * FROM cordscript_vars")
532
+ for row in cur.fetchall():
533
+ all_data[f"{row[0]}_{row[1]}"] = row[2]
534
+ elif kind == "better_sqlite3":
535
+ rows = db.prepare("SELECT * FROM cordscript_vars").all()
536
+ for row in rows:
537
+ all_data[f"{row['user_id']}_{row['key']}"] = row['value']
538
+
539
+ if not path:
540
+ return json.dumps(all_data, indent=2, ensure_ascii=False)
541
+
542
+ results = {}
543
+ for key, value in all_data.items():
544
+ if path.lower() in key.lower() or path.lower() in str(value).lower():
545
+ results[key] = value
546
+
547
+ if results:
548
+ return json.dumps(results, indent=2, ensure_ascii=False)
549
+ else:
550
+ return f"[No results found for '{path}']"
551
+ except Exception as e:
552
+ return f"[Error in $searchDB: {e}]"
553
+
554
+ case "wipedb":
555
+ try:
556
+ kind = _detect_db(db)
557
+ with SuppressOutput():
558
+ if kind == "motor":
559
+ await db["cordscript_vars"].delete_many({})
560
+ elif kind == "pymongo":
561
+ db["cordscript_vars"].delete_many({})
562
+ elif kind in ("asyncpg", "psycopg"):
563
+ await db.execute("DELETE FROM cordscript_vars")
564
+ elif kind == "sqlite3":
565
+ cur = db.cursor()
566
+ cur.execute("DELETE FROM cordscript_vars")
567
+ db.commit()
568
+ elif kind == "better_sqlite3":
569
+ db.prepare("DELETE FROM cordscript_vars").run()
570
+ return "[Database wiped successfully]"
571
+ except Exception as e:
572
+ return f"[Error in $wipeDB: {e}]"
573
+
574
+ case "getuserleaderboardid":
575
+ name = _key(0)
576
+ sort_type = _key(1) or "DESC"
577
+ position = int(_key(2)) if _key(2) else 1
578
+
579
+ if not name:
580
+ return "[Error in $getUserLeaderboardID: name required]"
581
+
582
+ try:
583
+ kind = _detect_db(db)
584
+ all_data = {}
585
+
586
+ with SuppressOutput():
587
+ if kind == "motor":
588
+ cursor = db["cordscript_vars"].find({})
589
+ async for doc in cursor:
590
+ doc.pop("_id", None)
591
+ all_data[f"{doc['user_id']}_{doc['key']}"] = doc['value']
592
+ elif kind == "pymongo":
593
+ for doc in db["cordscript_vars"].find({}):
594
+ doc.pop("_id", None)
595
+ all_data[f"{doc['user_id']}_{doc['key']}"] = doc['value']
596
+ elif kind in ("asyncpg", "psycopg"):
597
+ rows = await db.fetch("SELECT * FROM cordscript_vars")
598
+ for row in rows:
599
+ all_data[f"{row['user_id']}_{row['key']}"] = row['value']
600
+ elif kind == "sqlite3":
601
+ cur = db.cursor()
602
+ cur.execute("SELECT * FROM cordscript_vars")
603
+ for row in cur.fetchall():
604
+ all_data[f"{row[0]}_{row[1]}"] = row[2]
605
+ elif kind == "better_sqlite3":
606
+ rows = db.prepare("SELECT * FROM cordscript_vars").all()
607
+ for row in rows:
608
+ all_data[f"{row['user_id']}_{row['key']}"] = row['value']
609
+
610
+ leaderboard = {}
611
+ for key, value in all_data.items():
612
+ if f"_{name}" in key:
613
+ user_id = key.split("_")[0]
614
+ try:
615
+ val = float(value)
616
+ leaderboard[user_id] = val
617
+ except (ValueError, TypeError):
618
+ pass
619
+
620
+ if sort_type.upper() == "ASC":
621
+ sorted_lb = sorted(leaderboard.items(), key=lambda x: x[1])
622
+ else:
623
+ sorted_lb = sorted(leaderboard.items(), key=lambda x: x[1], reverse=True)
624
+
625
+ if 1 <= position <= len(sorted_lb):
626
+ return sorted_lb[position - 1][0]
627
+ else:
628
+ return "[Position not found]"
629
+ except Exception as e:
630
+ return f"[Error in $getUserLeaderboardID: {e}]"
631
+
632
+ case "userleaderboard":
633
+ name = _key(0)
634
+ sort_type = _key(1) or "DESC"
635
+ max_items = int(_key(2)) if _key(2) else 10
636
+ page = int(_key(3)) if _key(3) else 1
637
+ separator = _val(4) or "\n"
638
+ env_value = _key(5) or "{value}"
639
+ env_position = _key(6) or "{position}"
640
+ code = _val(7) or "{position}. {user_id} - {value}"
641
+
642
+ if not name:
643
+ return "[Error in $userLeaderboard: name required]"
644
+
645
+ try:
646
+ kind = _detect_db(db)
647
+ all_data = {}
648
+
649
+ with SuppressOutput():
650
+ if kind == "motor":
651
+ cursor = db["cordscript_vars"].find({})
652
+ async for doc in cursor:
653
+ doc.pop("_id", None)
654
+ all_data[f"{doc['user_id']}_{doc['key']}"] = doc['value']
655
+ elif kind == "pymongo":
656
+ for doc in db["cordscript_vars"].find({}):
657
+ doc.pop("_id", None)
658
+ all_data[f"{doc['user_id']}_{doc['key']}"] = doc['value']
659
+ elif kind in ("asyncpg", "psycopg"):
660
+ rows = await db.fetch("SELECT * FROM cordscript_vars")
661
+ for row in rows:
662
+ all_data[f"{row['user_id']}_{row['key']}"] = row['value']
663
+ elif kind == "sqlite3":
664
+ cur = db.cursor()
665
+ cur.execute("SELECT * FROM cordscript_vars")
666
+ for row in cur.fetchall():
667
+ all_data[f"{row[0]}_{row[1]}"] = row[2]
668
+ elif kind == "better_sqlite3":
669
+ rows = db.prepare("SELECT * FROM cordscript_vars").all()
670
+ for row in rows:
671
+ all_data[f"{row['user_id']}_{row['key']}"] = row['value']
672
+
673
+ leaderboard = {}
674
+ for key, value in all_data.items():
675
+ if f"_{name}" in key:
676
+ user_id = key.split("_")[0]
677
+ try:
678
+ val = float(value)
679
+ leaderboard[user_id] = val
680
+ except (ValueError, TypeError):
681
+ pass
682
+
683
+ if sort_type.upper() == "ASC":
684
+ sorted_lb = sorted(leaderboard.items(), key=lambda x: x[1])
685
+ else:
686
+ sorted_lb = sorted(leaderboard.items(), key=lambda x: x[1], reverse=True)
687
+
688
+ start = (page - 1) * max_items
689
+ end = start + max_items
690
+ page_data = sorted_lb[start:end]
691
+
692
+ if not page_data:
693
+ return "[No data for this page]"
694
+
695
+ result = []
696
+ for pos, (user_id, value) in enumerate(page_data, start=start + 1):
697
+ line = code.replace(env_value, str(value))
698
+ line = line.replace(env_position, str(pos))
699
+ line = line.replace("{user_id}", user_id)
700
+ line = line.replace("{value}", str(value))
701
+ line = line.replace("{position}", str(pos))
702
+ result.append(line)
703
+
704
+ return separator.join(result)
705
+ except Exception as e:
706
+ return f"[Error in $userLeaderboard: {e}]"
707
+
708
+ case "getuserleaderboardlength":
709
+ name = _key(0)
710
+ length_type = _key(1) or "count"
711
+ decimals = int(_key(2)) if _key(2) else 0
712
+
713
+ if not name:
714
+ return "[Error in $getUserLeaderboardLength: name required]"
715
+
716
+ try:
717
+ kind = _detect_db(db)
718
+ all_data = {}
719
+
720
+ with SuppressOutput():
721
+ if kind == "motor":
722
+ cursor = db["cordscript_vars"].find({})
723
+ async for doc in cursor:
724
+ doc.pop("_id", None)
725
+ all_data[f"{doc['user_id']}_{doc['key']}"] = doc['value']
726
+ elif kind == "pymongo":
727
+ for doc in db["cordscript_vars"].find({}):
728
+ doc.pop("_id", None)
729
+ all_data[f"{doc['user_id']}_{doc['key']}"] = doc['value']
730
+ elif kind in ("asyncpg", "psycopg"):
731
+ rows = await db.fetch("SELECT * FROM cordscript_vars")
732
+ for row in rows:
733
+ all_data[f"{row['user_id']}_{row['key']}"] = row['value']
734
+ elif kind == "sqlite3":
735
+ cur = db.cursor()
736
+ cur.execute("SELECT * FROM cordscript_vars")
737
+ for row in cur.fetchall():
738
+ all_data[f"{row[0]}_{row[1]}"] = row[2]
739
+ elif kind == "better_sqlite3":
740
+ rows = db.prepare("SELECT * FROM cordscript_vars").all()
741
+ for row in rows:
742
+ all_data[f"{row['user_id']}_{row['key']}"] = row['value']
743
+
744
+ values = []
745
+ for key, value in all_data.items():
746
+ if f"_{name}" in key:
747
+ try:
748
+ val = float(value)
749
+ values.append(val)
750
+ except (ValueError, TypeError):
751
+ pass
752
+
753
+ if length_type.lower() == "count":
754
+ result = len(values)
755
+ elif length_type.lower() == "sum":
756
+ result = sum(values) if values else 0
757
+ elif length_type.lower() == "avg":
758
+ result = sum(values) / len(values) if values else 0
759
+ elif length_type.lower() == "max":
760
+ result = max(values) if values else 0
761
+ elif length_type.lower() == "min":
762
+ result = min(values) if values else 0
763
+ else:
764
+ result = len(values)
765
+
766
+ if decimals > 0:
767
+ result = round(result, decimals)
768
+ else:
769
+ result = int(result)
770
+
771
+ return str(result)
772
+ except Exception as e:
773
+ return f"[Error in $getUserLeaderboardLength: {e}]"
774
+
775
+ case "getuserleaderboardvalue":
776
+ name = _key(0)
777
+ sort_type = _key(1) or "DESC"
778
+ user_id = _key(2)
779
+
780
+ if not name:
781
+ return "[Error in $getUserLeaderboardValue: name required]"
782
+
783
+ if not user_id:
784
+ import discord
785
+ ctx = parser.ctx
786
+ user_id = str(ctx.user.id) if isinstance(ctx, discord.Interaction) else str(getattr(ctx, "author", ctx).id)
787
+
788
+ try:
789
+ kind = _detect_db(db)
790
+ all_data = {}
791
+
792
+ with SuppressOutput():
793
+ if kind == "motor":
794
+ cursor = db["cordscript_vars"].find({})
795
+ async for doc in cursor:
796
+ doc.pop("_id", None)
797
+ all_data[f"{doc['user_id']}_{doc['key']}"] = doc['value']
798
+ elif kind == "pymongo":
799
+ for doc in db["cordscript_vars"].find({}):
800
+ doc.pop("_id", None)
801
+ all_data[f"{doc['user_id']}_{doc['key']}"] = doc['value']
802
+ elif kind in ("asyncpg", "psycopg"):
803
+ rows = await db.fetch("SELECT * FROM cordscript_vars")
804
+ for row in rows:
805
+ all_data[f"{row['user_id']}_{row['key']}"] = row['value']
806
+ elif kind == "sqlite3":
807
+ cur = db.cursor()
808
+ cur.execute("SELECT * FROM cordscript_vars")
809
+ for row in cur.fetchall():
810
+ all_data[f"{row[0]}_{row[1]}"] = row[2]
811
+ elif kind == "better_sqlite3":
812
+ rows = db.prepare("SELECT * FROM cordscript_vars").all()
813
+ for row in rows:
814
+ all_data[f"{row['user_id']}_{row['key']}"] = row['value']
815
+
816
+ leaderboard = {}
817
+ for key, value in all_data.items():
818
+ if f"_{name}" in key:
819
+ uid = key.split("_")[0]
820
+ try:
821
+ val = float(value)
822
+ leaderboard[uid] = val
823
+ except (ValueError, TypeError):
824
+ pass
825
+
826
+ if str(user_id) not in leaderboard:
827
+ return "[User not in leaderboard]"
828
+
829
+ user_value = leaderboard[str(user_id)]
830
+
831
+ if sort_type.upper() == "ASC":
832
+ sorted_lb = sorted(leaderboard.items(), key=lambda x: x[1])
833
+ else:
834
+ sorted_lb = sorted(leaderboard.items(), key=lambda x: x[1], reverse=True)
835
+
836
+ position = next((i + 1 for i, (uid, _) in enumerate(sorted_lb) if uid == str(user_id)), None)
837
+
838
+ return f"{user_value}|{position}"
839
+ except Exception as e:
840
+ return f"[Error in $getUserLeaderboardValue: {e}]"
841
+
842
+ case _:
843
+ return None
844
+
845
+ except Exception as e:
846
+ return f"[Unexpected error in ${fn}: {e}]"