@abdarrahmanabdelnasir/relay-node 0.1.19 → 0.1.21

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.
@@ -35,6 +35,12 @@ const client = new Client({
35
35
 
36
36
  const relay = new RelayClient({ apiKey, baseUrl, hmacSecret });
37
37
 
38
+ // Set botId from env immediately (for config enforcement)
39
+ if (botId) {
40
+ relay.botId = parseInt(botId, 10);
41
+ console.log(`[commandless] Bot ID set from env: ${relay.botId}`);
42
+ }
43
+
38
44
  useDiscordAdapter({ client, relay, mentionRequired: true });
39
45
 
40
46
  client.once('ready', async () => {
@@ -46,11 +52,17 @@ client.once('ready', async () => {
46
52
  clientId: client.user.id,
47
53
  botId: parseInt(botId, 10)
48
54
  });
49
- if (registeredBotId) relay.botId = registeredBotId;
50
- console.log(`[commandless] Bot registered with ID: ${registeredBotId}`);
55
+ // Update botId if registration succeeded and returned a different ID
56
+ if (registeredBotId) {
57
+ relay.botId = registeredBotId;
58
+ console.log(`[commandless] Bot registered with ID: ${registeredBotId}`);
59
+ } else {
60
+ console.log(`[commandless] Registration returned null, using env BOT_ID: ${relay.botId}`);
61
+ }
51
62
  } catch (e) {
52
63
  console.error('[commandless] registerBot failed:', e?.message || e);
53
- process.exit(1);
64
+ // Don't exit - botId is already set from env, so config enforcement will still work
65
+ console.log(`[commandless] Continuing with botId from env: ${relay.botId}`);
54
66
  }
55
67
  setInterval(async () => { try { await relay.heartbeat(); } catch {} }, 30_000);
56
68
  });
@@ -40,8 +40,19 @@ export function useDiscordAdapter(opts) {
40
40
  memberRoles,
41
41
  });
42
42
  if (!filterResult.allowed) {
43
- // Silently ignore (filtered by config)
44
- console.log(`[commandless] Message filtered: ${filterResult.reason}`);
43
+ // If this is a premium-only feature and the user is not premium,
44
+ // send a friendly explanation instead of silently ignoring.
45
+ if (filterResult.reason === 'Premium only') {
46
+ try {
47
+ await message.reply("This command is part of this bot's premium features, and your account is not marked as premium for this bot.");
48
+ }
49
+ catch {
50
+ // ignore reply failures
51
+ }
52
+ }
53
+ else {
54
+ console.log(`[commandless] Message filtered: ${filterResult.reason}`);
55
+ }
45
56
  return;
46
57
  }
47
58
  }
@@ -70,6 +81,7 @@ export function useDiscordAdapter(opts) {
70
81
  content: message.content,
71
82
  timestamp: message.createdTimestamp,
72
83
  botClientId: client.user?.id,
84
+ botId: relay.botId || undefined, // Include botId for backend config enforcement
73
85
  isReplyToBot,
74
86
  referencedMessageId: message.reference?.messageId,
75
87
  referencedMessageAuthorId: message.reference ? client.user?.id : undefined,
@@ -121,6 +133,7 @@ export function useDiscordAdapter(opts) {
121
133
  return out;
122
134
  })(),
123
135
  timestamp: Date.now(),
136
+ botId: relay.botId || undefined, // Include botId for backend config enforcement
124
137
  };
125
138
  try {
126
139
  const dec = await relay.sendEvent(evt);
@@ -10,6 +10,7 @@ export interface BotConfig {
10
10
  enabledUsers: string[];
11
11
  disabledUsers: string[];
12
12
  premiumRoleIds: string[];
13
+ premiumUserIds: string[];
13
14
  enabledCommandCategories: string[];
14
15
  disabledCommands: string[];
15
16
  commandMode: 'all' | 'category_based' | 'whitelist' | 'blacklist';
@@ -137,7 +137,9 @@ export class ConfigCache {
137
137
  // Check permission mode
138
138
  switch (this.config.permissionMode) {
139
139
  case 'premium_only': {
140
- const isPremium = roles.some(roleId => this.config.premiumRoleIds.includes(roleId));
140
+ const isPremiumRole = roles.some(roleId => this.config.premiumRoleIds.includes(roleId));
141
+ const isPremiumUser = (this.config.premiumUserIds || []).includes(userId);
142
+ const isPremium = isPremiumRole || isPremiumUser;
141
143
  if (!isPremium) {
142
144
  return { allowed: false, reason: 'Premium only' };
143
145
  }
@@ -172,7 +174,9 @@ export class ConfigCache {
172
174
  return { allowed: true };
173
175
  const now = Date.now();
174
176
  const roles = memberRoles || [];
175
- const isPremium = roles.some(roleId => this.config.premiumRoleIds.includes(roleId));
177
+ const isPremiumRole = roles.some(roleId => this.config.premiumRoleIds.includes(roleId));
178
+ const isPremiumUser = (this.config.premiumUserIds || []).includes(userId);
179
+ const isPremium = isPremiumRole || isPremiumUser;
176
180
  // User rate limit
177
181
  const userLimit = isPremium ? this.config.premiumRateLimit : this.config.freeRateLimit;
178
182
  const userKey = `user:${userId}`;
package/dist/types.d.ts CHANGED
@@ -8,6 +8,7 @@ export type RelayEvent = {
8
8
  content: string;
9
9
  timestamp: number;
10
10
  botClientId?: Snowflake;
11
+ botId?: number | string;
11
12
  isReplyToBot?: boolean;
12
13
  referencedMessageId?: string;
13
14
  referencedMessageAuthorId?: Snowflake;
@@ -22,6 +23,7 @@ export type RelayEvent = {
22
23
  options?: Record<string, unknown>;
23
24
  timestamp: number;
24
25
  botClientId?: Snowflake;
26
+ botId?: number | string;
25
27
  };
26
28
  export interface Decision {
27
29
  id: string;
@@ -43,8 +43,19 @@ function useDiscordAdapter(opts) {
43
43
  memberRoles,
44
44
  });
45
45
  if (!filterResult.allowed) {
46
- // Silently ignore (filtered by config)
47
- console.log(`[commandless] Message filtered: ${filterResult.reason}`);
46
+ // If this is a premium-only feature and the user is not premium,
47
+ // send a friendly explanation instead of silently ignoring.
48
+ if (filterResult.reason === 'Premium only') {
49
+ try {
50
+ await message.reply("This command is part of this bot's premium features, and your account is not marked as premium for this bot.");
51
+ }
52
+ catch {
53
+ // ignore reply failures
54
+ }
55
+ }
56
+ else {
57
+ console.log(`[commandless] Message filtered: ${filterResult.reason}`);
58
+ }
48
59
  return;
49
60
  }
50
61
  }
@@ -73,6 +84,7 @@ function useDiscordAdapter(opts) {
73
84
  content: message.content,
74
85
  timestamp: message.createdTimestamp,
75
86
  botClientId: client.user?.id,
87
+ botId: relay.botId || undefined, // Include botId for backend config enforcement
76
88
  isReplyToBot,
77
89
  referencedMessageId: message.reference?.messageId,
78
90
  referencedMessageAuthorId: message.reference ? client.user?.id : undefined,
@@ -124,6 +136,7 @@ function useDiscordAdapter(opts) {
124
136
  return out;
125
137
  })(),
126
138
  timestamp: Date.now(),
139
+ botId: relay.botId || undefined, // Include botId for backend config enforcement
127
140
  };
128
141
  try {
129
142
  const dec = await relay.sendEvent(evt);
@@ -10,6 +10,7 @@ export interface BotConfig {
10
10
  enabledUsers: string[];
11
11
  disabledUsers: string[];
12
12
  premiumRoleIds: string[];
13
+ premiumUserIds: string[];
13
14
  enabledCommandCategories: string[];
14
15
  disabledCommands: string[];
15
16
  commandMode: 'all' | 'category_based' | 'whitelist' | 'blacklist';
@@ -140,7 +140,9 @@ class ConfigCache {
140
140
  // Check permission mode
141
141
  switch (this.config.permissionMode) {
142
142
  case 'premium_only': {
143
- const isPremium = roles.some(roleId => this.config.premiumRoleIds.includes(roleId));
143
+ const isPremiumRole = roles.some(roleId => this.config.premiumRoleIds.includes(roleId));
144
+ const isPremiumUser = (this.config.premiumUserIds || []).includes(userId);
145
+ const isPremium = isPremiumRole || isPremiumUser;
144
146
  if (!isPremium) {
145
147
  return { allowed: false, reason: 'Premium only' };
146
148
  }
@@ -175,7 +177,9 @@ class ConfigCache {
175
177
  return { allowed: true };
176
178
  const now = Date.now();
177
179
  const roles = memberRoles || [];
178
- const isPremium = roles.some(roleId => this.config.premiumRoleIds.includes(roleId));
180
+ const isPremiumRole = roles.some(roleId => this.config.premiumRoleIds.includes(roleId));
181
+ const isPremiumUser = (this.config.premiumUserIds || []).includes(userId);
182
+ const isPremium = isPremiumRole || isPremiumUser;
179
183
  // User rate limit
180
184
  const userLimit = isPremium ? this.config.premiumRateLimit : this.config.freeRateLimit;
181
185
  const userKey = `user:${userId}`;
@@ -8,6 +8,7 @@ export type RelayEvent = {
8
8
  content: string;
9
9
  timestamp: number;
10
10
  botClientId?: Snowflake;
11
+ botId?: number | string;
11
12
  isReplyToBot?: boolean;
12
13
  referencedMessageId?: string;
13
14
  referencedMessageAuthorId?: Snowflake;
@@ -22,6 +23,7 @@ export type RelayEvent = {
22
23
  options?: Record<string, unknown>;
23
24
  timestamp: number;
24
25
  botClientId?: Snowflake;
26
+ botId?: number | string;
25
27
  };
26
28
  export interface Decision {
27
29
  id: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abdarrahmanabdelnasir/relay-node",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "dist/index.js",