ragebuttonapi 1.0.0 → 1.0.2

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.
@@ -5,13 +5,13 @@
5
5
  * const rageApi = require('ragebuttonapi');
6
6
  *
7
7
  * // one-time init
8
- * rageApi.init({ url: 'https://ragebutton.yoursite.com', apiKey: 'rbapi_xxx' });
8
+ * rageApi.init({ apiKey: 'rbapi_xxx' });
9
9
  *
10
10
  * // register rage
11
11
  * await rageApi.rage('this stupid bug omg');
12
12
  *
13
- * // or inline config
14
- * await rageApi.rage('why', { url: '...', apiKey: '...' });
13
+ * // or inline config override
14
+ * await rageApi.rage('why', { apiKey: '...' });
15
15
  *
16
16
  * // get global stats
17
17
  * const stats = await rageApi.stats();
@@ -26,9 +26,9 @@ const https = require('https');
26
26
  const http = require('http');
27
27
  const url = require('url');
28
28
 
29
- let _globalConfig = { url: null, apiKey: null };
29
+ const DEFAULT_SERVER = 'https://ragebutton.unaux.com';
30
+ let _globalConfig = { url: DEFAULT_SERVER, apiKey: null };
30
31
 
31
- // ─── low-level request helper ────────────────────────────────────────────────
32
32
  function request(endpoint, { method = 'GET', body = null } = {}) {
33
33
  return new Promise((resolve, reject) => {
34
34
  const parsed = url.parse(endpoint);
@@ -45,7 +45,9 @@ function request(endpoint, { method = 'GET', body = null } = {}) {
45
45
  path : parsed.path,
46
46
  method,
47
47
  headers : {
48
- 'User-Agent': 'RAGEBUTTONAPI-npm/1.0',
48
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
49
+ 'Accept': 'application/json, text/plain, */*',
50
+ 'Accept-Language': 'en-US,en;q=0.9',
49
51
  ...(payload ? {
50
52
  'Content-Type' : 'application/x-www-form-urlencoded',
51
53
  'Content-Length': Buffer.byteLength(payload),
@@ -68,7 +70,14 @@ function request(endpoint, { method = 'GET', body = null } = {}) {
68
70
  resolve(json);
69
71
  }
70
72
  } catch {
71
- reject(new Error(`bad response from server (status ${res.statusCode}). is ur server up?`));
73
+ // Detect if server is running on InfinityFree / Unaux and blocking programmatic calls
74
+ if (data.includes('__test') || data.includes('aes.js') || data.includes('Javascript to work') || data.includes('testcookie')) {
75
+ reject(new Error(
76
+ 'API request blocked by your hosting provider (Unaux/InfinityFree). Free hosts use a browser verification cookie (aes.js) that blocks standard CLI/API client scripts. To fix this, consider deploying your API on developer-friendly platforms like Koyeb, Render, Railway, or Vercel (for serverless PHP) which do not block API calls.'
77
+ ));
78
+ } else {
79
+ reject(new Error(`bad response from server (status ${res.statusCode}). is ur server up?`));
80
+ }
72
81
  }
73
82
  });
74
83
  });
@@ -79,13 +88,11 @@ function request(endpoint, { method = 'GET', body = null } = {}) {
79
88
  });
80
89
  }
81
90
 
82
- // ─── resolve config, merging inline overrides ────────────────────────────────
83
91
  function resolveConfig(inline = {}) {
84
92
  const cfg = {
85
- url : inline.url ?? _globalConfig.url,
93
+ url : inline.url ?? _globalConfig.url ?? DEFAULT_SERVER,
86
94
  apiKey: inline.apiKey ?? _globalConfig.apiKey,
87
95
  };
88
- if (!cfg.url) throw new Error('ragebuttonapi: server url is required. call rageApi.init({ url }) or pass it inline.');
89
96
  if (!cfg.apiKey) throw new Error('ragebuttonapi: apiKey is required. get it from ur dashboard.');
90
97
  return { ...cfg, url: cfg.url.replace(/\/$/, '') };
91
98
  }
@@ -94,7 +101,7 @@ function resolveConfig(inline = {}) {
94
101
 
95
102
  /**
96
103
  * One-time global config. call this at app startup.
97
- * @param {{ url: string, apiKey: string }} config
104
+ * @param {{ url?: string, apiKey: string }} config
98
105
  */
99
106
  function init(config = {}) {
100
107
  if (config.url) _globalConfig.url = config.url.replace(/\/$/, '');
@@ -125,8 +132,7 @@ async function rage(reason, config = {}) {
125
132
  * @returns {Promise<{total_rages, today_rages, user_count}>}
126
133
  */
127
134
  async function stats(config = {}) {
128
- const serverUrl = config.url ?? _globalConfig.url;
129
- if (!serverUrl) throw new Error('ragebuttonapi: url is required for stats()');
135
+ const serverUrl = config.url ?? _globalConfig.url ?? DEFAULT_SERVER;
130
136
  return request(`${serverUrl.replace(/\/$/, '')}/api/stats.php`);
131
137
  }
132
138
 
@@ -137,9 +143,8 @@ async function stats(config = {}) {
137
143
  * @returns {Promise<{ feed: Array<{user, reason, time_ago}> }>}
138
144
  */
139
145
  async function feed(limit = 10, config = {}) {
140
- const serverUrl = config.url ?? _globalConfig.url;
141
- if (!serverUrl) throw new Error('ragebuttonapi: url is required for feed()');
146
+ const serverUrl = config.url ?? _globalConfig.url ?? DEFAULT_SERVER;
142
147
  return request(`${serverUrl.replace(/\/$/, '')}/api/feed.php?limit=${limit}`);
143
148
  }
144
149
 
145
- module.exports = { init, rage, stats, feed };
150
+ module.exports = { init, rage, stats, feed };
@@ -4,7 +4,7 @@
4
4
  * install: npm install -g ragebuttonapi
5
5
  * usage:
6
6
  * rage → register a rage (prompts for reason)
7
- * rage setup → configure server url + api key
7
+ * rage setup → configure api key (server is automatically set)
8
8
  * rage "this bug is insane" → rage with inline reason
9
9
  * rage --reason "msg" → rage with reason flag
10
10
  * rage stats → global stats
@@ -21,23 +21,28 @@ const os = require('os');
21
21
  const rageApi = require('./index.js');
22
22
 
23
23
  const CONFIG_PATH = path.join(os.homedir(), '.ragebuttonapirc');
24
- const VERSION = '1.0.0';
24
+ const VERSION = '1.0.1';
25
+ const DEFAULT_SERVER = 'https://ragebutton.unaux.com';
25
26
 
26
27
  // ─── Config file helpers ──────────────────────────────────────────────────────
27
28
  function loadConfig() {
28
29
  try {
29
30
  if (fs.existsSync(CONFIG_PATH)) {
30
- return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
31
+ const parsed = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
32
+ // Ensure there is always a default url
33
+ if (!parsed.url) {
34
+ parsed.url = DEFAULT_SERVER;
35
+ }
36
+ return parsed;
31
37
  }
32
38
  } catch { /* corrupt config, treat as missing */ }
33
- return {};
39
+ return { url: DEFAULT_SERVER };
34
40
  }
35
41
 
36
42
  function saveConfig(cfg) {
37
43
  fs.writeFileSync(CONFIG_PATH, JSON.stringify(cfg, null, 2), { mode: 0o600 });
38
44
  }
39
45
 
40
- // ─── Colors (no deps, raw ANSI) ───────────────────────────────────────────────
41
46
  const c = {
42
47
  reset : '\x1b[0m',
43
48
  bold : '\x1b[1m',
@@ -64,7 +69,6 @@ ${dim('────────────────────────
64
69
  `);
65
70
  }
66
71
 
67
- // ─── Prompts (pure readline, no deps) ────────────────────────────────────────
68
72
  function prompt(question) {
69
73
  return new Promise(resolve => {
70
74
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
@@ -75,7 +79,6 @@ function prompt(question) {
75
79
  function promptPassword(question) {
76
80
  return new Promise(resolve => {
77
81
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
78
- // hide input
79
82
  process.stdout.write(question);
80
83
  process.stdin.setRawMode?.(true);
81
84
  let pass = '';
@@ -104,18 +107,10 @@ function promptPassword(question) {
104
107
  });
105
108
  }
106
109
 
107
- // ─── Setup command ────────────────────────────────────────────────────────────
108
110
  async function cmdSetup(existing = {}) {
109
111
  logo();
110
- console.log(`${acc('Setup')} — lets get u configured fr\n`);
111
-
112
- const defaultUrl = existing.url || '';
113
- let serverUrl = await prompt(
114
- `Server URL ${defaultUrl ? dim(`[${defaultUrl}]`) : ''}: `
115
- );
116
- if (!serverUrl && defaultUrl) serverUrl = defaultUrl;
117
- if (!serverUrl) { console.log(err('\nurls cant be empty. try again.')); process.exit(1); }
118
- serverUrl = serverUrl.replace(/\/$/, '');
112
+ console.log(`${acc('Setup')} — let's get u configured fr\n`);
113
+ console.log(`${dim('Server is locked to default:')} ${hl(DEFAULT_SERVER)}`);
119
114
 
120
115
  const defaultKey = existing.apiKey ? '***' + existing.apiKey.slice(-4) : '';
121
116
  console.log(`\nAPI Key (from ur dashboard)${defaultKey ? dim(` [${defaultKey}]`) : ''}:`);
@@ -123,29 +118,27 @@ async function cmdSetup(existing = {}) {
123
118
  try {
124
119
  apiKey = await promptPassword('> ');
125
120
  } catch {
126
- // fallback if raw mode unavailable (piped stdin etc)
127
121
  apiKey = await prompt('API Key: ');
128
122
  }
129
123
  if (!apiKey && existing.apiKey) apiKey = existing.apiKey;
130
124
  if (!apiKey) { console.log(err('\napi key cant be empty bestie.')); process.exit(1); }
131
125
 
132
- const cfg = { url: serverUrl, apiKey };
126
+ const cfg = { url: DEFAULT_SERVER, apiKey };
133
127
  saveConfig(cfg);
134
128
 
135
129
  console.log(`\n${ok('✓')} saved to ${dim(CONFIG_PATH)}`);
136
- console.log(`\n url: ${hl(serverUrl)}`);
137
- console.log(` key: ${dim('***' + apiKey.slice(-4))}\n`);
130
+ console.log(` server: ${hl(DEFAULT_SERVER)}`);
131
+ console.log(` key : ${dim('***' + apiKey.slice(-4))}\n`);
138
132
  console.log(ok(`all done! run ${hl('rage')} anytime to register ur suffering 💢\n`));
139
133
  }
140
134
 
141
- // ─── Rage command ─────────────────────────────────────────────────────────────
142
135
  async function cmdRage(reason, cfg) {
143
- if (!cfg.url || !cfg.apiKey) {
136
+ const serverUrl = cfg.url || DEFAULT_SERVER;
137
+ if (!cfg.apiKey) {
144
138
  console.log(`\n${err('not configured yet fr.')} run ${hl('rage setup')} first.\n`);
145
139
  process.exit(1);
146
140
  }
147
141
 
148
- // ask for reason if not provided inline
149
142
  if (reason === null || reason === undefined) {
150
143
  reason = await prompt(`${dim('what broke? (enter to skip):')} `);
151
144
  }
@@ -153,10 +146,10 @@ async function cmdRage(reason, cfg) {
153
146
  process.stdout.write(`\n${acc('💢')} registering ur rage...`);
154
147
 
155
148
  try {
156
- rageApi.init({ url: cfg.url, apiKey: cfg.apiKey });
149
+ rageApi.init({ url: serverUrl, apiKey: cfg.apiKey });
157
150
  const res = await rageApi.rage(reason, { source: 'cli' });
158
151
 
159
- process.stdout.write('\r' + ' '.repeat(40) + '\r'); // clear the "registering..." line
152
+ process.stdout.write('\r' + ' '.repeat(40) + '\r');
160
153
 
161
154
  console.log(`\n ${acc('💢 RAGE REGISTERED')}\n`);
162
155
  console.log(` ${ok('✓')} message: ${dim(res.message)}`);
@@ -187,7 +180,7 @@ async function cmdRage(reason, cfg) {
187
180
  } else if (e.code === 'daily_limit') {
188
181
  console.log(`\n ${err('😤 daily limit hit')} — 24/24 rages today. iconic. come back tomorrow.\n`);
189
182
  } else if (e.code === 'ECONNREFUSED' || e.code === 'ENOTFOUND') {
190
- console.log(`\n ${err('❌ couldnt connect')} — is ur server up? check the url in ur config (${dim('rage setup')} to edit)\n`);
183
+ console.log(`\n ${err('❌ couldnt connect')} — is the server down? (${dim(serverUrl)})\n`);
191
184
  } else {
192
185
  console.log(`\n ${err('💀 error:')} ${e.message}\n`);
193
186
  }
@@ -195,14 +188,10 @@ async function cmdRage(reason, cfg) {
195
188
  }
196
189
  }
197
190
 
198
- // ─── Stats command ────────────────────────────────────────────────────────────
199
191
  async function cmdStats(cfg) {
200
- if (!cfg.url) {
201
- console.log(`\n${err('no url configured.')} run ${hl('rage setup')} first.\n`);
202
- process.exit(1);
203
- }
192
+ const serverUrl = cfg.url || DEFAULT_SERVER;
204
193
  try {
205
- const res = await rageApi.stats({ url: cfg.url });
194
+ const res = await rageApi.stats({ url: serverUrl });
206
195
  logo();
207
196
  console.log(` ${acc('📊 global stats')}\n`);
208
197
  console.log(` total rages: ${hl(Number(res.total_rages).toLocaleString())}`);
@@ -215,14 +204,10 @@ async function cmdStats(cfg) {
215
204
  }
216
205
  }
217
206
 
218
- // ─── Feed command ─────────────────────────────────────────────────────────────
219
207
  async function cmdFeed(limit, cfg) {
220
- if (!cfg.url) {
221
- console.log(`\n${err('no url configured.')} run ${hl('rage setup')} first.\n`);
222
- process.exit(1);
223
- }
208
+ const serverUrl = cfg.url || DEFAULT_SERVER;
224
209
  try {
225
- const res = await rageApi.feed(limit, { url: cfg.url });
210
+ const res = await rageApi.feed(limit, { url: serverUrl });
226
211
  const feed = res.feed || [];
227
212
  logo();
228
213
  console.log(` ${acc('🔴 live rage feed')} ${dim(`(last ${feed.length})`)} \n`);
@@ -245,12 +230,11 @@ async function cmdFeed(limit, cfg) {
245
230
  }
246
231
  }
247
232
 
248
- // ─── Help ─────────────────────────────────────────────────────────────────────
249
233
  function showHelp() {
250
234
  logo();
251
235
  console.log(` ${acc('Commands')}\n`);
252
236
  console.log(` ${hl('rage')} → register rage (asks reason interactively)`);
253
- console.log(` ${hl('rage setup')} → configure server url + api key`);
237
+ console.log(` ${hl('rage setup')} → configure api key`);
254
238
  console.log(` ${hl('rage stats')} → global stats`);
255
239
  console.log(` ${hl('rage feed')} → live rage feed`);
256
240
  console.log(` ${hl('rage "reason here"')} → rage with inline reason`);
@@ -263,7 +247,6 @@ function showHelp() {
263
247
  console.log('');
264
248
  }
265
249
 
266
- // ─── Main ─────────────────────────────────────────────────────────────────────
267
250
  async function main() {
268
251
  const args = process.argv.slice(2);
269
252
  const cfg = loadConfig();
@@ -289,7 +272,6 @@ async function main() {
289
272
  }
290
273
 
291
274
  // default: rage
292
- // inline reason: first non-flag, non-subcommand arg
293
275
  const inlineReason = args.find(a => !a.startsWith('-') && a !== 'rage');
294
276
 
295
277
  let reason;
@@ -304,4 +286,4 @@ async function main() {
304
286
  main().catch(e => {
305
287
  console.error(`\n${err('unexpected error:')} ${e.message}\n`);
306
288
  process.exit(1);
307
- });
289
+ });
package/package.json CHANGED
@@ -1,29 +1 @@
1
- {
2
- "name": "ragebuttonapi",
3
- "version": "1.0.0",
4
- "description": "official npm package + cli for RAGEBUTTONAPI™ — register developer rage from ur terminal or node app. no account needed for anon rages.",
5
- "main": "index.js",
6
- "bin": {
7
- "rage": "cli.js",
8
- "npm-cli": "cli.js",
9
- "npmc": "cli.js"
10
- },
11
- "scripts": {
12
- "test": "node cli.js --help"
13
- },
14
- "keywords": [
15
- "rage",
16
- "developer",
17
- "api",
18
- "cli",
19
- "fun",
20
- "mental-health",
21
- "programming",
22
- "anger"
23
- ],
24
- "author": "RAGEBUTTONAPI™",
25
- "license": "MIT",
26
- "engines": {
27
- "node": ">=16.0.0"
28
- }
29
- }
1
+ {"name":"ragebuttonapi","version":"1.0.2","description":"official npm package + cli for RAGEBUTTONAPI™ — register developer rage from ur terminal or node app. no account needed for anon rages.","main":"index.js","bin":{"rage":"./cli.js"},"scripts":{"test":"node cli.js --help"},"keywords":["rage","developer","api","cli","fun","mental-health","programming","anger"],"author":"RAGEBUTTONAPI™","license":"MIT","engines":{"node":">=16.0.0"},"dependencies":{}}
package/npm-cli.tar.gz DELETED
Binary file
package/npm-cli.zip DELETED
Binary file