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,179 @@
1
+ from __future__ import annotations
2
+ import asyncio
3
+ import re
4
+ import discord
5
+ from datetime import timedelta
6
+ from ...core.context import get_guild
7
+
8
+ _DURATION_PATTERN = re.compile(r"(\d+)\s*([smhdw])", re.IGNORECASE)
9
+ _DURATION_UNITS = {"s": 1, "m": 60, "h": 3600, "d": 86400, "w": 604800}
10
+
11
+ _PENDING_UNBANS: dict[tuple[int, int], asyncio.Task] = {}
12
+
13
+
14
+ def _parse_duration(text: str) -> int | None:
15
+ if not text or not text.strip():
16
+ return None
17
+ matches = _DURATION_PATTERN.findall(text.strip())
18
+ if not matches:
19
+ return None
20
+ total = 0
21
+ for value, unit in matches:
22
+ total += int(value) * _DURATION_UNITS[unit.lower()]
23
+ return total if total > 0 else None
24
+
25
+
26
+ def _cancel_pending_unban(guild_id: int, user_id: int):
27
+ task = _PENDING_UNBANS.pop((guild_id, user_id), None)
28
+ if task and not task.done():
29
+ task.cancel()
30
+
31
+
32
+ def _schedule_unban(guild, user_id: int, delay: int):
33
+ _cancel_pending_unban(guild.id, user_id)
34
+
35
+ async def _runner():
36
+ try:
37
+ await asyncio.sleep(delay)
38
+ await guild.unban(discord.Object(id=user_id), reason="Tempo de banimento expirado")
39
+ except asyncio.CancelledError:
40
+ pass
41
+ except discord.NotFound:
42
+ pass
43
+ except Exception as e:
44
+ print(f"[cordscript] Failed to auto-unban {user_id}: {e}")
45
+ finally:
46
+ _PENDING_UNBANS.pop((guild.id, user_id), None)
47
+
48
+ _PENDING_UNBANS[(guild.id, user_id)] = asyncio.create_task(_runner())
49
+
50
+
51
+ async def handle_moderation(fn: str, args: list[str], raw: str, parser) -> str | None:
52
+ if fn not in ("ban", "unban", "kick", "timeout", "untimeout"):
53
+ return None
54
+
55
+ guild = get_guild(parser.ctx)
56
+ if guild is None:
57
+ return f"[Error in ${fn}: no guild in this context]"
58
+
59
+ try:
60
+ match fn:
61
+ case "ban":
62
+ user_id = args[0].strip() if args else ""
63
+ reason = args[1] if len(args) > 1 and args[1].strip() else None
64
+ duration_raw = args[2] if len(args) > 2 else ""
65
+ if not user_id:
66
+ return "[Error in $ban: user id required]"
67
+ try:
68
+ uid = int(user_id)
69
+ except ValueError:
70
+ return "[Error in $ban: invalid user id]"
71
+ duration = _parse_duration(duration_raw)
72
+ if duration_raw.strip() and duration is None:
73
+ return "[Error in $ban: invalid time format, use e.g. 7s, 1m, 10h, 1d]"
74
+ try:
75
+ await guild.ban(discord.Object(id=uid), reason=reason)
76
+ except discord.Forbidden:
77
+ return "[Error in $ban: missing permissions]"
78
+ except discord.HTTPException as e:
79
+ return f"[Error in $ban: {e}]"
80
+ if duration:
81
+ _schedule_unban(guild, uid, duration)
82
+ return ""
83
+
84
+ case "unban":
85
+ user_id = args[0].strip() if args else ""
86
+ reason = args[1] if len(args) > 1 and args[1].strip() else None
87
+ if not user_id:
88
+ return "[Error in $unban: user id required]"
89
+ try:
90
+ uid = int(user_id)
91
+ except ValueError:
92
+ return "[Error in $unban: invalid user id]"
93
+ _cancel_pending_unban(guild.id, uid)
94
+ try:
95
+ await guild.unban(discord.Object(id=uid), reason=reason)
96
+ except discord.Forbidden:
97
+ return "[Error in $unban: missing permissions]"
98
+ except discord.NotFound:
99
+ return "[Error in $unban: user is not banned]"
100
+ except discord.HTTPException as e:
101
+ return f"[Error in $unban: {e}]"
102
+ return ""
103
+
104
+ case "kick":
105
+ user_id = args[0].strip() if args else ""
106
+ reason = args[1] if len(args) > 1 and args[1].strip() else None
107
+ if not user_id:
108
+ return "[Error in $kick: user id required]"
109
+ try:
110
+ uid = int(user_id)
111
+ except ValueError:
112
+ return "[Error in $kick: invalid user id]"
113
+ member = guild.get_member(uid)
114
+ if member is None:
115
+ try:
116
+ member = await guild.fetch_member(uid)
117
+ except discord.NotFound:
118
+ return "[Error in $kick: member not found]"
119
+ try:
120
+ await guild.kick(member, reason=reason)
121
+ except discord.Forbidden:
122
+ return "[Error in $kick: missing permissions]"
123
+ except discord.HTTPException as e:
124
+ return f"[Error in $kick: {e}]"
125
+ return ""
126
+
127
+ case "timeout":
128
+ user_id = args[0].strip() if args else ""
129
+ reason = args[1] if len(args) > 1 and args[1].strip() else None
130
+ duration_raw = args[2] if len(args) > 2 else ""
131
+ if not user_id:
132
+ return "[Error in $timeout: user id required]"
133
+ duration = _parse_duration(duration_raw)
134
+ if duration is None:
135
+ return "[Error in $timeout: invalid or missing time, use e.g. 7s, 1m, 10h, 1d]"
136
+ try:
137
+ uid = int(user_id)
138
+ except ValueError:
139
+ return "[Error in $timeout: invalid user id]"
140
+ member = guild.get_member(uid)
141
+ if member is None:
142
+ try:
143
+ member = await guild.fetch_member(uid)
144
+ except discord.NotFound:
145
+ return "[Error in $timeout: member not found]"
146
+ try:
147
+ until = discord.utils.utcnow() + timedelta(seconds=duration)
148
+ await member.timeout(until, reason=reason)
149
+ except discord.Forbidden:
150
+ return "[Error in $timeout: missing permissions]"
151
+ except discord.HTTPException as e:
152
+ return f"[Error in $timeout: {e}]"
153
+ return ""
154
+
155
+ case "untimeout":
156
+ user_id = args[0].strip() if args else ""
157
+ reason = args[1] if len(args) > 1 and args[1].strip() else None
158
+ if not user_id:
159
+ return "[Error in $untimeout: user id required]"
160
+ try:
161
+ uid = int(user_id)
162
+ except ValueError:
163
+ return "[Error in $untimeout: invalid user id]"
164
+ member = guild.get_member(uid)
165
+ if member is None:
166
+ try:
167
+ member = await guild.fetch_member(uid)
168
+ except discord.NotFound:
169
+ return "[Error in $untimeout: member not found]"
170
+ try:
171
+ await member.timeout(None, reason=reason)
172
+ except discord.Forbidden:
173
+ return "[Error in $untimeout: missing permissions]"
174
+ except discord.HTTPException as e:
175
+ return f"[Error in $untimeout: {e}]"
176
+ return ""
177
+
178
+ except Exception as e:
179
+ return f"[Unexpected error in ${fn}: {e}]"
@@ -0,0 +1,56 @@
1
+ from __future__ import annotations
2
+ import discord
3
+
4
+ _STATUS_MAP = {
5
+ "online": discord.Status.online,
6
+ "idle": discord.Status.idle,
7
+ "dnd": discord.Status.dnd,
8
+ "invisible": discord.Status.invisible,
9
+ }
10
+
11
+ _TYPE_MAP = {
12
+ "competing": discord.ActivityType.competing,
13
+ "watching": discord.ActivityType.watching,
14
+ "listening": discord.ActivityType.listening,
15
+ }
16
+
17
+
18
+ async def handle_status(fn: str, args: list[str], raw: str, parser) -> str | None:
19
+ if fn != "setstatus":
20
+ return None
21
+
22
+ presence_raw = args[0].strip().lower() if len(args) > 0 else ""
23
+ type_raw = args[1].strip().lower() if len(args) > 1 else ""
24
+ name = args[2] if len(args) > 2 else ""
25
+ state = args[3] if len(args) > 3 and args[3].strip() else None
26
+ url = args[4].strip() if len(args) > 4 and args[4].strip() else None
27
+
28
+ status = _STATUS_MAP.get(presence_raw)
29
+ if status is None:
30
+ return "[Error in $setStatus: invalid presence, use online, idle, dnd or invisible]"
31
+
32
+ if type_raw not in ("competing", "custom", "watching", "streaming", "listening"):
33
+ return "[Error in $setStatus: invalid type, use competing, custom, watching, streaming or listening]"
34
+
35
+ try:
36
+ if type_raw == "custom":
37
+ text = state or name
38
+ if not text:
39
+ return "[Error in $setStatus: name or state required for custom]"
40
+ activity = discord.CustomActivity(name=text)
41
+ elif type_raw == "streaming":
42
+ if not name or not url:
43
+ return "[Error in $setStatus: name and url required for streaming]"
44
+ activity = discord.Streaming(name=name, url=url)
45
+ else:
46
+ if not name:
47
+ return "[Error in $setStatus: name required]"
48
+ activity = discord.Activity(type=_TYPE_MAP[type_raw], name=name, state=state)
49
+
50
+ await parser.client.change_presence(status=status, activity=activity)
51
+ except discord.HTTPException as e:
52
+ return f"[Error in $setStatus: {e}]"
53
+ except Exception as e:
54
+ return f"[Unexpected error in $setStatus: {e}]"
55
+
56
+ return ""
@@ -0,0 +1,3 @@
1
+ from .user import handle_user
2
+
3
+ __all__ = ["handle_user"]
@@ -0,0 +1,103 @@
1
+ from __future__ import annotations
2
+ from ...core.context import get_author, get_author_id, get_guild
3
+
4
+
5
+ def _resolve_member(parser, args, index=0):
6
+ guild = get_guild(parser.ctx)
7
+ if len(args) > index and args[index].strip():
8
+ target = args[index].strip()
9
+ if guild and target.isdigit():
10
+ member = guild.get_member(int(target))
11
+ if member:
12
+ return member
13
+ return None
14
+ return get_author(parser.ctx)
15
+
16
+
17
+ async def handle_user(fn: str, args: list[str], raw: str, parser) -> str | None:
18
+ try:
19
+ match fn:
20
+ case "authorid":
21
+ return get_author_id(parser.ctx)
22
+
23
+ case "author":
24
+ user = get_author(parser.ctx)
25
+ return str(user) if user else ""
26
+
27
+ case "username":
28
+ user = get_author(parser.ctx)
29
+ return user.name if user else ""
30
+
31
+ case "nickname":
32
+ user = _resolve_member(parser, args)
33
+ if user is None:
34
+ return ""
35
+ return getattr(user, "display_name", getattr(user, "name", ""))
36
+
37
+ case "mention":
38
+ user = _resolve_member(parser, args)
39
+ return user.mention if user else ""
40
+
41
+ case "avatar" | "useravatar":
42
+ user = _resolve_member(parser, args)
43
+ if user is None:
44
+ return ""
45
+ avatar = getattr(user, "display_avatar", None)
46
+ return str(avatar.url) if avatar else ""
47
+
48
+ case "tag" | "usertag":
49
+ user = _resolve_member(parser, args)
50
+ return str(user) if user else ""
51
+
52
+ case "isbot":
53
+ user = _resolve_member(parser, args)
54
+ return "true" if (user and getattr(user, "bot", False)) else "false"
55
+
56
+ case "userid":
57
+ user = _resolve_member(parser, args)
58
+ return str(user.id) if user else ""
59
+
60
+ case "joinedat":
61
+ user = _resolve_member(parser, args)
62
+ joined = getattr(user, "joined_at", None) if user else None
63
+ return str(int(joined.timestamp())) if joined else ""
64
+
65
+ case "createdat":
66
+ user = _resolve_member(parser, args)
67
+ created = getattr(user, "created_at", None) if user else None
68
+ return str(int(created.timestamp())) if created else ""
69
+
70
+ case "hasrole":
71
+ role_id = args[0].strip() if args else ""
72
+ user = _resolve_member(parser, args, index=1)
73
+ if not user or not role_id:
74
+ return "false"
75
+ roles = getattr(user, "roles", [])
76
+ return "true" if any(str(r.id) == role_id for r in roles) else "false"
77
+
78
+ case "highestrole":
79
+ user = _resolve_member(parser, args)
80
+ if not user:
81
+ return ""
82
+ roles = [r for r in getattr(user, "roles", []) if r.name != "@everyone"]
83
+ if not roles:
84
+ return ""
85
+ return max(roles, key=lambda r: r.position).name
86
+
87
+ case "membercount" | "guildmembercount":
88
+ guild = get_guild(parser.ctx)
89
+ return str(guild.member_count) if guild else ""
90
+
91
+ case "guildname" | "servername":
92
+ guild = get_guild(parser.ctx)
93
+ return guild.name if guild else ""
94
+
95
+ case "guildid" | "serverid":
96
+ guild = get_guild(parser.ctx)
97
+ return str(guild.id) if guild else ""
98
+
99
+ case _:
100
+ return None
101
+
102
+ except Exception as e:
103
+ return f"[Unexpected error in ${fn}: {e}]"
@@ -0,0 +1,14 @@
1
+ from .math import handle_math, safe_math
2
+ from .time import handle_time
3
+ from .random import handle_random
4
+ from .http import handle_http
5
+ from .cooldown import handle_cooldown
6
+ from .json import handle_json
7
+ from .strings import handle_strings
8
+
9
+ __all__ = [
10
+ "handle_math", "safe_math",
11
+ "handle_time", "handle_random",
12
+ "handle_http", "handle_cooldown",
13
+ "handle_json", "handle_strings",
14
+ ]
@@ -0,0 +1,35 @@
1
+ from __future__ import annotations
2
+ import time as time_module
3
+ from ...exceptions import CooldownError
4
+ from ...core.context import get_author_id
5
+
6
+ _COOLDOWNS: dict[str, float] = {}
7
+
8
+
9
+ async def handle_cooldown(fn: str, args: list[str], raw: str, parser) -> str | None:
10
+ try:
11
+ match fn:
12
+ case "cooldown":
13
+ try:
14
+ seconds = float(args[0].strip()) if args and args[0].strip() else 0
15
+ except ValueError:
16
+ return f"[Error in $cooldown: invalid duration]"
17
+ msg = args[1] if len(args) > 1 else ""
18
+ uid = get_author_id(parser.ctx)
19
+ cmd = getattr(parser, "_command_name", "unknown")
20
+ key = f"{uid}:{cmd}"
21
+ now = time_module.monotonic()
22
+ last = _COOLDOWNS.get(key, 0)
23
+ remaining = seconds - (now - last)
24
+ if remaining > 0:
25
+ raise CooldownError(msg or f"{round(remaining, 1)}s")
26
+ _COOLDOWNS[key] = now
27
+ return ""
28
+
29
+ case _:
30
+ return None
31
+
32
+ except CooldownError:
33
+ raise
34
+ except Exception as e:
35
+ return f"[Unexpected error in ${fn}: {e}]"
@@ -0,0 +1,73 @@
1
+ from __future__ import annotations
2
+ import json
3
+ import aiohttp
4
+
5
+
6
+ async def handle_http(fn: str, args: list[str], raw: str, parser) -> str | None:
7
+ try:
8
+ match fn:
9
+ case "httprequest" | "http":
10
+ url = args[0].strip() if args else ""
11
+ method = args[1].strip().upper() if len(args) > 1 and args[1].strip() else "GET"
12
+ body = args[2] if len(args) > 2 and args[2].strip() else None
13
+ headers_raw = args[3] if len(args) > 3 and args[3].strip() else None
14
+ key = args[4].strip() if len(args) > 4 and args[4].strip() else None
15
+ if not url:
16
+ return f"[Error in $http: URL required]"
17
+ try:
18
+ headers = json.loads(headers_raw) if headers_raw else {}
19
+ async with aiohttp.ClientSession() as session:
20
+ kwargs: dict = {"headers": headers}
21
+ if body:
22
+ try:
23
+ kwargs["json"] = json.loads(body)
24
+ except Exception:
25
+ kwargs["data"] = body
26
+ resp = await getattr(session, method.lower())(url, **kwargs)
27
+ text = await resp.text()
28
+ if key:
29
+ try:
30
+ parsed = json.loads(text)
31
+ keys = key.split(".")
32
+ val = parsed
33
+ for k in keys:
34
+ if isinstance(val, list):
35
+ val = val[int(k)]
36
+ else:
37
+ val = val[k]
38
+ return json.dumps(val) if isinstance(val, (dict, list)) else str(val)
39
+ except Exception as e:
40
+ return f"[Error in $http: key parse failed — {e}]"
41
+ return text
42
+ except Exception as e:
43
+ return f"[Error in $http: {e}]"
44
+
45
+ case "httpstatus":
46
+ url = args[0].strip() if args else ""
47
+ method = args[1].strip().upper() if len(args) > 1 and args[1].strip() else "GET"
48
+ if not url:
49
+ return f"[Error in $httpStatus: URL required]"
50
+ try:
51
+ async with aiohttp.ClientSession() as session:
52
+ resp = await getattr(session, method.lower())(url)
53
+ return str(resp.status)
54
+ except Exception as e:
55
+ return f"[Error in $httpStatus: {e}]"
56
+
57
+ case "httpheader":
58
+ url = args[0].strip() if args else ""
59
+ header = args[1].strip() if len(args) > 1 else ""
60
+ if not url or not header:
61
+ return f"[Error in $httpHeader: URL and header name required]"
62
+ try:
63
+ async with aiohttp.ClientSession() as session:
64
+ resp = await session.get(url)
65
+ return resp.headers.get(header, "")
66
+ except Exception as e:
67
+ return f"[Error in $httpHeader: {e}]"
68
+
69
+ case _:
70
+ return None
71
+
72
+ except Exception as e:
73
+ return f"[Unexpected error in ${fn}: {e}]"
@@ -0,0 +1,201 @@
1
+ from __future__ import annotations
2
+ import json as _json
3
+
4
+
5
+ async def handle_json(fn: str, args: list[str], raw: str, parser) -> str | None:
6
+ try:
7
+ match fn:
8
+ case "jsonparse":
9
+ raw_json = args[0] if args else ""
10
+ key = args[1].strip() if len(args) > 1 and args[1].strip() else None
11
+ try:
12
+ parsed = _json.loads(raw_json)
13
+ if key is None:
14
+ return _json.dumps(parsed)
15
+ keys = key.split(".")
16
+ val = parsed
17
+ for k in keys:
18
+ val = val[int(k)] if isinstance(val, list) else val[k]
19
+ return _json.dumps(val) if isinstance(val, (dict, list)) else str(val)
20
+ except Exception as e:
21
+ return f"[Error in $jsonParse: {e}]"
22
+
23
+ case "jsonset":
24
+ raw_json = args[0] if args else "{}"
25
+ key = args[1].strip() if len(args) > 1 else ""
26
+ value = args[2] if len(args) > 2 else ""
27
+ try:
28
+ parsed = _json.loads(raw_json)
29
+ keys = key.split(".")
30
+ obj = parsed
31
+ for k in keys[:-1]:
32
+ obj = obj[k]
33
+ try:
34
+ obj[keys[-1]] = _json.loads(value)
35
+ except Exception:
36
+ obj[keys[-1]] = value
37
+ return _json.dumps(parsed)
38
+ except Exception as e:
39
+ return f"[Error in $jsonSet: {e}]"
40
+
41
+ case "jsonkeys":
42
+ try:
43
+ return _json.dumps(list(_json.loads(args[0] if args else "{}").keys()))
44
+ except Exception as e:
45
+ return f"[Error in $jsonKeys: {e}]"
46
+
47
+ case "jsonvalues":
48
+ try:
49
+ return _json.dumps(list(_json.loads(args[0] if args else "{}").values()))
50
+ except Exception as e:
51
+ return f"[Error in $jsonValues: {e}]"
52
+
53
+ case "jsondelete":
54
+ raw_json = args[0] if args else "{}"
55
+ key = args[1].strip() if len(args) > 1 else ""
56
+ try:
57
+ parsed = _json.loads(raw_json)
58
+ keys = key.split(".")
59
+ obj = parsed
60
+ for k in keys[:-1]:
61
+ obj = obj[k]
62
+ del obj[keys[-1]]
63
+ return _json.dumps(parsed)
64
+ except Exception as e:
65
+ return f"[Error in $jsonDelete: {e}]"
66
+
67
+ case "arraycreate":
68
+ return _json.dumps(list(args))
69
+
70
+ case "arraypush":
71
+ raw_arr = args[0] if args else "[]"
72
+ value = args[1] if len(args) > 1 else ""
73
+ try:
74
+ arr = _json.loads(raw_arr)
75
+ try:
76
+ arr.append(_json.loads(value))
77
+ except Exception:
78
+ arr.append(value)
79
+ return _json.dumps(arr)
80
+ except Exception as e:
81
+ return f"[Error in $arrayPush: {e}]"
82
+
83
+ case "arraypop":
84
+ try:
85
+ arr = _json.loads(args[0] if args else "[]")
86
+ arr.pop()
87
+ return _json.dumps(arr)
88
+ except Exception as e:
89
+ return f"[Error in $arrayPop: {e}]"
90
+
91
+ case "arrayshift":
92
+ try:
93
+ arr = _json.loads(args[0] if args else "[]")
94
+ arr.pop(0)
95
+ return _json.dumps(arr)
96
+ except Exception as e:
97
+ return f"[Error in $arrayShift: {e}]"
98
+
99
+ case "arrayget":
100
+ try:
101
+ index = int(args[1].strip()) - 1 if len(args) > 1 else 0
102
+ arr = _json.loads(args[0] if args else "[]")
103
+ val = arr[index]
104
+ return _json.dumps(val) if isinstance(val, (dict, list)) else str(val)
105
+ except Exception as e:
106
+ return f"[Error in $arrayGet: {e}]"
107
+
108
+ case "arrayset":
109
+ try:
110
+ index = int(args[1].strip()) - 1 if len(args) > 1 else 0
111
+ value = args[2] if len(args) > 2 else ""
112
+ arr = _json.loads(args[0] if args else "[]")
113
+ try:
114
+ arr[index] = _json.loads(value)
115
+ except Exception:
116
+ arr[index] = value
117
+ return _json.dumps(arr)
118
+ except Exception as e:
119
+ return f"[Error in $arraySet: {e}]"
120
+
121
+ case "arrayremove":
122
+ try:
123
+ index = int(args[1].strip()) - 1 if len(args) > 1 else 0
124
+ arr = _json.loads(args[0] if args else "[]")
125
+ arr.pop(index)
126
+ return _json.dumps(arr)
127
+ except Exception as e:
128
+ return f"[Error in $arrayRemove: {e}]"
129
+
130
+ case "arrayincludes":
131
+ try:
132
+ arr = _json.loads(args[0] if args else "[]")
133
+ value = args[1] if len(args) > 1 else ""
134
+ try:
135
+ val = _json.loads(value)
136
+ except Exception:
137
+ val = value
138
+ return "true" if val in arr else "false"
139
+ except Exception as e:
140
+ return f"[Error in $arrayIncludes: {e}]"
141
+
142
+ case "arraylength":
143
+ try:
144
+ return str(len(_json.loads(args[0] if args else "[]")))
145
+ except Exception as e:
146
+ return f"[Error in $arrayLength: {e}]"
147
+
148
+ case "arrayjoin":
149
+ sep = args[1] if len(args) > 1 else ","
150
+ try:
151
+ return sep.join(str(x) for x in _json.loads(args[0] if args else "[]"))
152
+ except Exception as e:
153
+ return f"[Error in $arrayJoin: {e}]"
154
+
155
+ case "arraysort":
156
+ order = args[1].strip().lower() if len(args) > 1 else "asc"
157
+ try:
158
+ arr = _json.loads(args[0] if args else "[]")
159
+ arr.sort(reverse=(order == "desc"))
160
+ return _json.dumps(arr)
161
+ except Exception as e:
162
+ return f"[Error in $arraySort: {e}]"
163
+
164
+ case "arrayreverse":
165
+ try:
166
+ arr = _json.loads(args[0] if args else "[]")
167
+ arr.reverse()
168
+ return _json.dumps(arr)
169
+ except Exception as e:
170
+ return f"[Error in $arrayReverse: {e}]"
171
+
172
+ case "arrayfind":
173
+ try:
174
+ arr = _json.loads(args[0] if args else "[]")
175
+ value = args[1] if len(args) > 1 else ""
176
+ try:
177
+ val = _json.loads(value)
178
+ except Exception:
179
+ val = value
180
+ return str(arr.index(val) + 1) if val in arr else "-1"
181
+ except Exception as e:
182
+ return f"[Error in $arrayFind: {e}]"
183
+
184
+ case "arrayfromjson":
185
+ raw_json = args[0] if args else "{}"
186
+ key = args[1].strip() if len(args) > 1 else ""
187
+ try:
188
+ parsed = _json.loads(raw_json)
189
+ if key:
190
+ for k in key.split("."):
191
+ parsed = parsed[k]
192
+ return _json.dumps(parsed if isinstance(parsed, list) else list(parsed))
193
+ return _json.dumps(list(parsed.values()))
194
+ except Exception as e:
195
+ return f"[Error in $arrayFromJson: {e}]"
196
+
197
+ case _:
198
+ return None
199
+
200
+ except Exception as e:
201
+ return f"[Unexpected error in ${fn}: {e}]"