nerimity-sdk 0.1.0__tar.gz

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. nerimity_sdk-0.1.0/PKG-INFO +232 -0
  2. nerimity_sdk-0.1.0/README.md +208 -0
  3. nerimity_sdk-0.1.0/nerimity_sdk/__init__.py +51 -0
  4. nerimity_sdk-0.1.0/nerimity_sdk/bot.py +275 -0
  5. nerimity_sdk-0.1.0/nerimity_sdk/cache/__init__.py +0 -0
  6. nerimity_sdk-0.1.0/nerimity_sdk/cache/redis_adapter.py +27 -0
  7. nerimity_sdk-0.1.0/nerimity_sdk/cache/store.py +105 -0
  8. nerimity_sdk-0.1.0/nerimity_sdk/cli/__init__.py +0 -0
  9. nerimity_sdk-0.1.0/nerimity_sdk/cli/main.py +119 -0
  10. nerimity_sdk-0.1.0/nerimity_sdk/commands/__init__.py +0 -0
  11. nerimity_sdk-0.1.0/nerimity_sdk/commands/builders.py +114 -0
  12. nerimity_sdk-0.1.0/nerimity_sdk/commands/prefix.py +45 -0
  13. nerimity_sdk-0.1.0/nerimity_sdk/commands/router.py +180 -0
  14. nerimity_sdk-0.1.0/nerimity_sdk/context/__init__.py +0 -0
  15. nerimity_sdk-0.1.0/nerimity_sdk/context/ctx.py +125 -0
  16. nerimity_sdk-0.1.0/nerimity_sdk/devtools/__init__.py +0 -0
  17. nerimity_sdk-0.1.0/nerimity_sdk/devtools/watcher.py +63 -0
  18. nerimity_sdk-0.1.0/nerimity_sdk/events/__init__.py +0 -0
  19. nerimity_sdk-0.1.0/nerimity_sdk/events/emitter.py +47 -0
  20. nerimity_sdk-0.1.0/nerimity_sdk/events/payloads.py +332 -0
  21. nerimity_sdk-0.1.0/nerimity_sdk/models.py +244 -0
  22. nerimity_sdk-0.1.0/nerimity_sdk/permissions/__init__.py +0 -0
  23. nerimity_sdk-0.1.0/nerimity_sdk/permissions/checker.py +50 -0
  24. nerimity_sdk-0.1.0/nerimity_sdk/plugins/__init__.py +0 -0
  25. nerimity_sdk-0.1.0/nerimity_sdk/plugins/manager.py +138 -0
  26. nerimity_sdk-0.1.0/nerimity_sdk/scheduler.py +66 -0
  27. nerimity_sdk-0.1.0/nerimity_sdk/storage.py +156 -0
  28. nerimity_sdk-0.1.0/nerimity_sdk/testing.py +83 -0
  29. nerimity_sdk-0.1.0/nerimity_sdk/transport/__init__.py +0 -0
  30. nerimity_sdk-0.1.0/nerimity_sdk/transport/gateway.py +138 -0
  31. nerimity_sdk-0.1.0/nerimity_sdk/transport/rest.py +200 -0
  32. nerimity_sdk-0.1.0/nerimity_sdk/utils/__init__.py +0 -0
  33. nerimity_sdk-0.1.0/nerimity_sdk/utils/logging.py +51 -0
  34. nerimity_sdk-0.1.0/pyproject.toml +29 -0
  35. nerimity_sdk-0.1.0/pytest.ini +2 -0
  36. nerimity_sdk-0.1.0/tests/__init__.py +0 -0
  37. nerimity_sdk-0.1.0/tests/test_sdk.py +371 -0
@@ -0,0 +1,232 @@
1
+ Metadata-Version: 2.4
2
+ Name: nerimity-sdk
3
+ Version: 0.1.0
4
+ Summary: A fully-featured Python SDK for building Nerimity bots
5
+ License: MIT
6
+ Keywords: bot,chat,nerimity,sdk
7
+ Requires-Python: >=3.10
8
+ Requires-Dist: aiohttp>=3.9
9
+ Requires-Dist: python-socketio[asyncio-client]>=5.11
10
+ Provides-Extra: cron
11
+ Requires-Dist: croniter>=2.0; extra == 'cron'
12
+ Provides-Extra: dev
13
+ Requires-Dist: croniter>=2.0; extra == 'dev'
14
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
15
+ Requires-Dist: pytest>=8.0; extra == 'dev'
16
+ Requires-Dist: watchfiles>=0.21; extra == 'dev'
17
+ Provides-Extra: redis
18
+ Requires-Dist: redis>=5.0; extra == 'redis'
19
+ Provides-Extra: sqlite
20
+ Requires-Dist: aiosqlite>=0.19; extra == 'sqlite'
21
+ Provides-Extra: watch
22
+ Requires-Dist: watchfiles>=0.21; extra == 'watch'
23
+ Description-Content-Type: text/markdown
24
+
25
+ # nerimity-sdk
26
+
27
+ A fully-featured Python SDK for building [Nerimity](https://nerimity.com) bots. Think discord.py, but for Nerimity.
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ pip install nerimity-sdk
33
+ ```
34
+
35
+ With Redis support for multi-process bots:
36
+ ```bash
37
+ pip install "nerimity-sdk[redis]"
38
+ ```
39
+
40
+ ## Quickstart
41
+
42
+ ```bash
43
+ nerimity create my-bot
44
+ cd my-bot
45
+ # edit bot.py and set your token
46
+ python bot.py
47
+ ```
48
+
49
+ Or manually:
50
+
51
+ ```python
52
+ from nerimity_sdk import Bot
53
+
54
+ bot = Bot(token="YOUR_TOKEN", prefix="!")
55
+
56
+ @bot.on("ready")
57
+ async def on_ready(me):
58
+ print(f"Logged in as {me.username}#{me.tag}")
59
+
60
+ @bot.command("ping", description="Replies with Pong!")
61
+ async def ping(ctx):
62
+ await ctx.reply("Pong!")
63
+
64
+ bot.run()
65
+ ```
66
+
67
+ ## Features
68
+
69
+ ### Core Transport
70
+ - Socket.IO gateway with **auto-reconnect** and **exponential backoff**
71
+ - **Event queue** so gateway bursts never drop events
72
+ - HTTP REST client with **per-route rate limit buckets** and `retry-after` handling
73
+
74
+ ### Event System
75
+ ```python
76
+ # Standard listener
77
+ @bot.on("message:created")
78
+ async def on_message(data): ...
79
+
80
+ # One-shot
81
+ @bot.once("ready")
82
+ async def on_first_ready(me): ...
83
+
84
+ # Wildcard — fires for every event
85
+ @bot.on("*")
86
+ async def log_all(data): ...
87
+ ```
88
+
89
+ Every handler runs in async isolation — one crashing handler won't affect others.
90
+
91
+ ### Commands
92
+ ```python
93
+ @bot.command(
94
+ "ban",
95
+ description="Ban a user",
96
+ usage="<user_id>",
97
+ category="Moderation",
98
+ guild_only=True,
99
+ required_user_perms=[Permissions.BAN_MEMBERS],
100
+ cooldown=5.0,
101
+ )
102
+ async def ban(ctx):
103
+ user_id = ctx.args[0]
104
+ await ctx.rest.ban_member(ctx.server_id, user_id)
105
+ await ctx.reply(f"Banned {user_id}")
106
+ ```
107
+
108
+ Flags: `!cmd --silent --count=3 "quoted arg"` → `ctx.flags["silent"]`, `ctx.args[0]`
109
+
110
+ ### Middleware
111
+ ```python
112
+ async def log_middleware(ctx, next):
113
+ print(f"Command from {ctx.author.username}")
114
+ await next(ctx)
115
+
116
+ bot.router.use(log_middleware)
117
+ ```
118
+
119
+ ### Context Object
120
+ ```python
121
+ ctx.message # Message object
122
+ ctx.author # User
123
+ ctx.channel_id # str
124
+ ctx.server # Server | None (from cache)
125
+ ctx.member # Member | None (from cache)
126
+ ctx.args # list[str]
127
+ ctx.flags # dict[str, Any]
128
+
129
+ await ctx.reply("hello")
130
+ await ctx.fetch_messages(limit=10)
131
+ await ctx.fetch_member(user_id)
132
+ ```
133
+
134
+ ### Cache
135
+ Automatic LRU/TTL cache for servers, channels, members, users, and messages. Partial gateway updates are merged intelligently — no stale overwrites.
136
+
137
+ ```python
138
+ server = bot.cache.servers.get("server_id")
139
+ user = bot.cache.users.get("user_id")
140
+ ```
141
+
142
+ ### Permissions
143
+ ```python
144
+ from nerimity_sdk import has_permission, Permissions
145
+
146
+ if has_permission(member, server, Permissions.KICK_MEMBERS):
147
+ ...
148
+ ```
149
+
150
+ ### Plugins
151
+ ```python
152
+ from nerimity_sdk import PluginBase, listener
153
+
154
+ class ModerationPlugin(PluginBase):
155
+ name = "moderation"
156
+
157
+ @listener("server:member_joined")
158
+ async def on_join(self, data):
159
+ print("Member joined:", data)
160
+
161
+ async def on_ready(self):
162
+ print("Moderation plugin ready!")
163
+
164
+ async def setup(bot):
165
+ await bot.plugins.load(ModerationPlugin())
166
+ ```
167
+
168
+ Hot reload at runtime:
169
+ ```python
170
+ await bot.plugins.reload("moderation")
171
+ ```
172
+
173
+ ### Debug Mode
174
+ ```python
175
+ bot = Bot(token="...", debug=True)
176
+ # Logs all raw gateway payloads with pretty JSON formatting
177
+ ```
178
+
179
+ ### Testing
180
+ ```python
181
+ from nerimity_sdk.testing import MockBot
182
+
183
+ bot = MockBot(prefix="!")
184
+
185
+ @bot.command("ping")
186
+ async def ping(ctx):
187
+ await ctx.reply("Pong!")
188
+
189
+ # In your test:
190
+ async def test_ping():
191
+ await bot.simulate_message("!ping")
192
+ bot.rest.create_message.assert_called_once()
193
+ ```
194
+
195
+ ### Sharding (stub)
196
+ ```python
197
+ # Architecture is shard-ready. When Nerimity adds sharding:
198
+ bot = Bot.from_shard(token="...", shard_id=0, shard_count=4)
199
+ ```
200
+
201
+ ## Project Structure
202
+
203
+ ```
204
+ nerimity_sdk/
205
+ ├── bot.py # Main Bot class
206
+ ├── models.py # Typed data models
207
+ ├── testing.py # MockBot + test helpers
208
+ ├── transport/
209
+ │ ├── gateway.py # Socket.IO client
210
+ │ └── rest.py # HTTP REST client
211
+ ├── events/
212
+ │ └── emitter.py # Async event emitter
213
+ ├── commands/
214
+ │ └── router.py # Prefix command router
215
+ ├── cache/
216
+ │ ├── store.py # LRU/TTL cache
217
+ │ └── redis_adapter.py # Optional Redis adapter
218
+ ├── context/
219
+ │ └── ctx.py # Context object
220
+ ├── permissions/
221
+ │ └── checker.py # Permission helpers
222
+ ├── plugins/
223
+ │ └── manager.py # Plugin system
224
+ ├── cli/
225
+ │ └── main.py # nerimity CLI
226
+ └── utils/
227
+ └── logging.py # Structured logging
228
+ ```
229
+
230
+ ## License
231
+
232
+ MIT
@@ -0,0 +1,208 @@
1
+ # nerimity-sdk
2
+
3
+ A fully-featured Python SDK for building [Nerimity](https://nerimity.com) bots. Think discord.py, but for Nerimity.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install nerimity-sdk
9
+ ```
10
+
11
+ With Redis support for multi-process bots:
12
+ ```bash
13
+ pip install "nerimity-sdk[redis]"
14
+ ```
15
+
16
+ ## Quickstart
17
+
18
+ ```bash
19
+ nerimity create my-bot
20
+ cd my-bot
21
+ # edit bot.py and set your token
22
+ python bot.py
23
+ ```
24
+
25
+ Or manually:
26
+
27
+ ```python
28
+ from nerimity_sdk import Bot
29
+
30
+ bot = Bot(token="YOUR_TOKEN", prefix="!")
31
+
32
+ @bot.on("ready")
33
+ async def on_ready(me):
34
+ print(f"Logged in as {me.username}#{me.tag}")
35
+
36
+ @bot.command("ping", description="Replies with Pong!")
37
+ async def ping(ctx):
38
+ await ctx.reply("Pong!")
39
+
40
+ bot.run()
41
+ ```
42
+
43
+ ## Features
44
+
45
+ ### Core Transport
46
+ - Socket.IO gateway with **auto-reconnect** and **exponential backoff**
47
+ - **Event queue** so gateway bursts never drop events
48
+ - HTTP REST client with **per-route rate limit buckets** and `retry-after` handling
49
+
50
+ ### Event System
51
+ ```python
52
+ # Standard listener
53
+ @bot.on("message:created")
54
+ async def on_message(data): ...
55
+
56
+ # One-shot
57
+ @bot.once("ready")
58
+ async def on_first_ready(me): ...
59
+
60
+ # Wildcard — fires for every event
61
+ @bot.on("*")
62
+ async def log_all(data): ...
63
+ ```
64
+
65
+ Every handler runs in async isolation — one crashing handler won't affect others.
66
+
67
+ ### Commands
68
+ ```python
69
+ @bot.command(
70
+ "ban",
71
+ description="Ban a user",
72
+ usage="<user_id>",
73
+ category="Moderation",
74
+ guild_only=True,
75
+ required_user_perms=[Permissions.BAN_MEMBERS],
76
+ cooldown=5.0,
77
+ )
78
+ async def ban(ctx):
79
+ user_id = ctx.args[0]
80
+ await ctx.rest.ban_member(ctx.server_id, user_id)
81
+ await ctx.reply(f"Banned {user_id}")
82
+ ```
83
+
84
+ Flags: `!cmd --silent --count=3 "quoted arg"` → `ctx.flags["silent"]`, `ctx.args[0]`
85
+
86
+ ### Middleware
87
+ ```python
88
+ async def log_middleware(ctx, next):
89
+ print(f"Command from {ctx.author.username}")
90
+ await next(ctx)
91
+
92
+ bot.router.use(log_middleware)
93
+ ```
94
+
95
+ ### Context Object
96
+ ```python
97
+ ctx.message # Message object
98
+ ctx.author # User
99
+ ctx.channel_id # str
100
+ ctx.server # Server | None (from cache)
101
+ ctx.member # Member | None (from cache)
102
+ ctx.args # list[str]
103
+ ctx.flags # dict[str, Any]
104
+
105
+ await ctx.reply("hello")
106
+ await ctx.fetch_messages(limit=10)
107
+ await ctx.fetch_member(user_id)
108
+ ```
109
+
110
+ ### Cache
111
+ Automatic LRU/TTL cache for servers, channels, members, users, and messages. Partial gateway updates are merged intelligently — no stale overwrites.
112
+
113
+ ```python
114
+ server = bot.cache.servers.get("server_id")
115
+ user = bot.cache.users.get("user_id")
116
+ ```
117
+
118
+ ### Permissions
119
+ ```python
120
+ from nerimity_sdk import has_permission, Permissions
121
+
122
+ if has_permission(member, server, Permissions.KICK_MEMBERS):
123
+ ...
124
+ ```
125
+
126
+ ### Plugins
127
+ ```python
128
+ from nerimity_sdk import PluginBase, listener
129
+
130
+ class ModerationPlugin(PluginBase):
131
+ name = "moderation"
132
+
133
+ @listener("server:member_joined")
134
+ async def on_join(self, data):
135
+ print("Member joined:", data)
136
+
137
+ async def on_ready(self):
138
+ print("Moderation plugin ready!")
139
+
140
+ async def setup(bot):
141
+ await bot.plugins.load(ModerationPlugin())
142
+ ```
143
+
144
+ Hot reload at runtime:
145
+ ```python
146
+ await bot.plugins.reload("moderation")
147
+ ```
148
+
149
+ ### Debug Mode
150
+ ```python
151
+ bot = Bot(token="...", debug=True)
152
+ # Logs all raw gateway payloads with pretty JSON formatting
153
+ ```
154
+
155
+ ### Testing
156
+ ```python
157
+ from nerimity_sdk.testing import MockBot
158
+
159
+ bot = MockBot(prefix="!")
160
+
161
+ @bot.command("ping")
162
+ async def ping(ctx):
163
+ await ctx.reply("Pong!")
164
+
165
+ # In your test:
166
+ async def test_ping():
167
+ await bot.simulate_message("!ping")
168
+ bot.rest.create_message.assert_called_once()
169
+ ```
170
+
171
+ ### Sharding (stub)
172
+ ```python
173
+ # Architecture is shard-ready. When Nerimity adds sharding:
174
+ bot = Bot.from_shard(token="...", shard_id=0, shard_count=4)
175
+ ```
176
+
177
+ ## Project Structure
178
+
179
+ ```
180
+ nerimity_sdk/
181
+ ├── bot.py # Main Bot class
182
+ ├── models.py # Typed data models
183
+ ├── testing.py # MockBot + test helpers
184
+ ├── transport/
185
+ │ ├── gateway.py # Socket.IO client
186
+ │ └── rest.py # HTTP REST client
187
+ ├── events/
188
+ │ └── emitter.py # Async event emitter
189
+ ├── commands/
190
+ │ └── router.py # Prefix command router
191
+ ├── cache/
192
+ │ ├── store.py # LRU/TTL cache
193
+ │ └── redis_adapter.py # Optional Redis adapter
194
+ ├── context/
195
+ │ └── ctx.py # Context object
196
+ ├── permissions/
197
+ │ └── checker.py # Permission helpers
198
+ ├── plugins/
199
+ │ └── manager.py # Plugin system
200
+ ├── cli/
201
+ │ └── main.py # nerimity CLI
202
+ └── utils/
203
+ └── logging.py # Structured logging
204
+ ```
205
+
206
+ ## License
207
+
208
+ MIT
@@ -0,0 +1,51 @@
1
+ """nerimity-sdk — A fully-featured Python SDK for building Nerimity bots."""
2
+ from nerimity_sdk.bot import Bot
3
+ from nerimity_sdk.models import (
4
+ User, UserBadge, Message, MessageAttachment,
5
+ Server, Channel, Member, Role, BotCommand, Permissions,
6
+ )
7
+ from nerimity_sdk.context.ctx import Context
8
+ from nerimity_sdk.commands.router import CommandRouter
9
+ from nerimity_sdk.commands.builders import MessageBuilder, Embed
10
+ from nerimity_sdk.commands.prefix import PrefixResolver, MemoryPrefixStore
11
+ from nerimity_sdk.events.emitter import EventEmitter
12
+ from nerimity_sdk.events.payloads import (
13
+ ReadyEvent, MessageCreatedEvent, MessageUpdatedEvent, MessageDeletedEvent,
14
+ ReactionAddedEvent, ReactionRemovedEvent, MemberJoinedEvent, MemberLeftEvent,
15
+ PresenceUpdatedEvent, TypingEvent,
16
+ )
17
+ from nerimity_sdk.cache.store import Cache
18
+ from nerimity_sdk.permissions.checker import (
19
+ has_permission, resolve_permissions, role_position, can_target,
20
+ )
21
+ from nerimity_sdk.plugins.manager import PluginBase, PluginManager, listener
22
+ from nerimity_sdk.storage import JsonStore, SqliteStore, RedisStore, MemoryStore
23
+ from nerimity_sdk.scheduler import Scheduler, CronJob
24
+
25
+ __version__ = "0.2.0"
26
+ __all__ = [
27
+ "Bot",
28
+ # Models
29
+ "User", "UserBadge", "Message", "MessageAttachment",
30
+ "Server", "Channel", "Member", "Role", "BotCommand", "Permissions",
31
+ # Context
32
+ "Context",
33
+ # Commands
34
+ "CommandRouter", "MessageBuilder", "Embed",
35
+ "PrefixResolver", "MemoryPrefixStore",
36
+ # Events
37
+ "EventEmitter",
38
+ "ReadyEvent", "MessageCreatedEvent", "MessageUpdatedEvent", "MessageDeletedEvent",
39
+ "ReactionAddedEvent", "ReactionRemovedEvent", "MemberJoinedEvent", "MemberLeftEvent",
40
+ "PresenceUpdatedEvent", "TypingEvent",
41
+ # Cache
42
+ "Cache",
43
+ # Permissions
44
+ "has_permission", "resolve_permissions", "role_position", "can_target",
45
+ # Plugins
46
+ "PluginBase", "PluginManager", "listener",
47
+ # Storage
48
+ "JsonStore", "SqliteStore", "RedisStore", "MemoryStore",
49
+ # Scheduler
50
+ "Scheduler", "CronJob",
51
+ ]