slashvibe-mcp 0.3.21 → 0.3.23

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 (235) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +280 -47
  3. package/auto-update.js +10 -15
  4. package/config.js +36 -31
  5. package/crypto.js +1 -6
  6. package/debug.js +12 -0
  7. package/discord.js +19 -19
  8. package/eslint.config.js +54 -0
  9. package/index.js +217 -207
  10. package/intelligence/index.js +2 -9
  11. package/intelligence/infer.js +10 -16
  12. package/intelligence/patterns.js +23 -18
  13. package/intelligence/proactive.js +16 -15
  14. package/intelligence/serendipity.js +57 -20
  15. package/memory.js +13 -8
  16. package/migrate-v2.js +72 -0
  17. package/notification-emitter.js +2 -2
  18. package/notify.js +39 -14
  19. package/package.json +28 -29
  20. package/post-install.js +141 -0
  21. package/presence.js +2 -2
  22. package/prompts.js +5 -9
  23. package/protocol/index.js +123 -87
  24. package/protocol/telegram-commands.js +36 -37
  25. package/store/api.js +358 -529
  26. package/store/local.js +9 -10
  27. package/store/profiles.js +48 -192
  28. package/store/reservations.js +2 -9
  29. package/store/skills.js +69 -71
  30. package/store/sqlite.js +355 -0
  31. package/test-skills-bootstrap.js +20 -0
  32. package/test-v2-integration.js +385 -0
  33. package/tools/_actions.js +48 -387
  34. package/tools/_connection-queue.js +45 -56
  35. package/tools/_discovery-enhanced.js +52 -57
  36. package/tools/_discovery.js +87 -185
  37. package/tools/{l2-status.js → _experimental/l2-status.js} +68 -70
  38. package/tools/{shipback.js → _experimental/shipback.js} +4 -3
  39. package/tools/_proactive-discovery.js +60 -73
  40. package/tools/_shared/index.js +41 -64
  41. package/tools/admin-inbox.js +10 -15
  42. package/tools/agents.js +1 -1
  43. package/tools/artifact-create.js +13 -23
  44. package/tools/artifact-view.js +4 -4
  45. package/tools/{_deprecated/back.js → back.js} +1 -1
  46. package/tools/bye.js +3 -5
  47. package/tools/consent.js +2 -2
  48. package/tools/context.js +9 -10
  49. package/tools/crossword.js +3 -2
  50. package/tools/discover.js +94 -356
  51. package/tools/dm.js +27 -86
  52. package/tools/doctor.js +12 -41
  53. package/tools/drawing.js +34 -20
  54. package/tools/echo.js +11 -11
  55. package/tools/feed.js +30 -58
  56. package/tools/follow.js +64 -187
  57. package/tools/{_deprecated/forget.js → forget.js} +4 -7
  58. package/tools/game.js +144 -48
  59. package/tools/handoff.js +6 -8
  60. package/tools/help.js +3 -3
  61. package/tools/idea.js +15 -27
  62. package/tools/inbox.js +121 -293
  63. package/tools/init.js +54 -151
  64. package/tools/invite.js +8 -21
  65. package/tools/migrate.js +27 -24
  66. package/tools/multiplayer-game.js +50 -40
  67. package/tools/{_deprecated/mute.js → mute.js} +4 -3
  68. package/tools/notifications.js +58 -48
  69. package/tools/observe.js +12 -15
  70. package/tools/onboarding.js +8 -11
  71. package/tools/open.js +13 -144
  72. package/tools/party-game.js +23 -12
  73. package/tools/patterns.js +2 -1
  74. package/tools/ping.js +5 -7
  75. package/tools/react.js +28 -30
  76. package/tools/{_deprecated/recall.js → recall.js} +5 -10
  77. package/tools/release.js +4 -2
  78. package/tools/{_deprecated/remember.js → remember.js} +4 -6
  79. package/tools/report.js +2 -2
  80. package/tools/request.js +6 -26
  81. package/tools/reserve.js +1 -1
  82. package/tools/session-fork.js +97 -0
  83. package/tools/session-save.js +109 -0
  84. package/tools/settings.js +30 -99
  85. package/tools/ship.js +74 -56
  86. package/tools/{_deprecated/skills-exchange.js → skills-exchange.js} +38 -39
  87. package/tools/social-inbox.js +22 -28
  88. package/tools/social-post.js +24 -27
  89. package/tools/solo-game.js +54 -46
  90. package/tools/start.js +14 -148
  91. package/tools/status.js +21 -68
  92. package/tools/submit.js +4 -2
  93. package/tools/suggest-tags.js +36 -33
  94. package/tools/summarize.js +19 -16
  95. package/tools/tag-suggestions.js +72 -73
  96. package/tools/test.js +1 -1
  97. package/tools/{_deprecated/tictactoe.js → tictactoe.js} +26 -26
  98. package/tools/token.js +4 -4
  99. package/tools/update.js +1 -2
  100. package/tools/watch.js +132 -112
  101. package/tools/who.js +20 -40
  102. package/tools/{_deprecated/wordassociation.js → wordassociation.js} +23 -20
  103. package/tools/workshop-buddy.js +52 -53
  104. package/tools/x-mentions.js +0 -1
  105. package/tools/x-reply.js +0 -1
  106. package/twitter.js +14 -20
  107. package/version.json +8 -10
  108. package/webhook-runner.js +132 -0
  109. package/auth-store.js +0 -148
  110. package/bridges/bridge-monitor.js +0 -388
  111. package/bridges/discord-bot.js +0 -431
  112. package/bridges/farcaster.js +0 -299
  113. package/bridges/telegram.js +0 -261
  114. package/bridges/webhook-health.js +0 -420
  115. package/bridges/webhook-server.js +0 -437
  116. package/bridges/whatsapp.js +0 -441
  117. package/bridges/x-webhook.js +0 -423
  118. package/games/arcade.js +0 -406
  119. package/games/chess.js +0 -451
  120. package/games/colorguess.js +0 -343
  121. package/games/crossword-words.js +0 -171
  122. package/games/crossword.js +0 -461
  123. package/games/drawing.js +0 -347
  124. package/games/gameroulette.js +0 -300
  125. package/games/gamerouter.js +0 -336
  126. package/games/gamestatus.js +0 -337
  127. package/games/guessnumber.js +0 -209
  128. package/games/hangman.js +0 -279
  129. package/games/memory.js +0 -338
  130. package/games/multiplayer-tictactoe.js +0 -389
  131. package/games/pixelart.js +0 -399
  132. package/games/quickduel.js +0 -354
  133. package/games/riddle.js +0 -371
  134. package/games/rockpaperscissors.js +0 -291
  135. package/games/snake.js +0 -406
  136. package/games/storybuilder.js +0 -343
  137. package/games/tictactoe.js +0 -345
  138. package/games/twentyquestions.js +0 -286
  139. package/games/twotruths.js +0 -207
  140. package/games/werewolf.js +0 -508
  141. package/games/wordassociation.js +0 -247
  142. package/games/wordchain.js +0 -135
  143. package/intelligence/interests.js +0 -369
  144. package/setup.js +0 -480
  145. package/smart-inbox.js +0 -276
  146. package/tools/_deprecated/auto-suggest-connections.js +0 -304
  147. package/tools/_deprecated/bootstrap-skills.js +0 -231
  148. package/tools/_deprecated/bridge-dashboard.js +0 -342
  149. package/tools/_deprecated/bridge-health.js +0 -400
  150. package/tools/_deprecated/bridge-live.js +0 -384
  151. package/tools/_deprecated/bridges.js +0 -383
  152. package/tools/_deprecated/colorguess.js +0 -281
  153. package/tools/_deprecated/discover-insights.js +0 -379
  154. package/tools/_deprecated/discover-momentum.js +0 -256
  155. package/tools/_deprecated/discovery-analytics.js +0 -345
  156. package/tools/_deprecated/discovery-auto-suggest.js +0 -275
  157. package/tools/_deprecated/discovery-bootstrap.js +0 -267
  158. package/tools/_deprecated/discovery-daily.js +0 -375
  159. package/tools/_deprecated/discovery-dashboard.js +0 -385
  160. package/tools/_deprecated/discovery-digest.js +0 -314
  161. package/tools/_deprecated/discovery-hub.js +0 -357
  162. package/tools/_deprecated/discovery-insights.js +0 -384
  163. package/tools/_deprecated/discovery-momentum.js +0 -281
  164. package/tools/_deprecated/discovery-monitor.js +0 -319
  165. package/tools/_deprecated/discovery-proactive.js +0 -300
  166. package/tools/_deprecated/draw.js +0 -317
  167. package/tools/_deprecated/farcaster.js +0 -307
  168. package/tools/_deprecated/games-catalog.js +0 -376
  169. package/tools/_deprecated/games.js +0 -313
  170. package/tools/_deprecated/guessnumber.js +0 -194
  171. package/tools/_deprecated/hangman.js +0 -129
  172. package/tools/_deprecated/multiplayer-tictactoe.js +0 -303
  173. package/tools/_deprecated/riddle.js +0 -240
  174. package/tools/_deprecated/run-bootstrap.js +0 -69
  175. package/tools/_deprecated/skills-analytics.js +0 -349
  176. package/tools/_deprecated/skills-bootstrap.js +0 -301
  177. package/tools/_deprecated/skills-dashboard.js +0 -268
  178. package/tools/_deprecated/skills.js +0 -380
  179. package/tools/_deprecated/smart-intro.js +0 -353
  180. package/tools/_deprecated/storybuilder.js +0 -331
  181. package/tools/_deprecated/telegram-bot.js +0 -183
  182. package/tools/_deprecated/telegram-setup.js +0 -214
  183. package/tools/_deprecated/twentyquestions.js +0 -143
  184. package/tools/_shared.js +0 -234
  185. package/tools/_work-context.js +0 -338
  186. package/tools/_work-context.manual-test.js +0 -199
  187. package/tools/_work-context.test.js +0 -260
  188. package/tools/activity.js +0 -220
  189. package/tools/agent-treasury.js +0 -288
  190. package/tools/analytics.js +0 -191
  191. package/tools/approve.js +0 -197
  192. package/tools/arcade.js +0 -173
  193. package/tools/artifacts-price.js +0 -107
  194. package/tools/ask-expert.js +0 -160
  195. package/tools/available.js +0 -120
  196. package/tools/become-expert.js +0 -150
  197. package/tools/broadcast.js +0 -325
  198. package/tools/chat.js +0 -202
  199. package/tools/collaborative-drawing.js +0 -286
  200. package/tools/connection-status.js +0 -178
  201. package/tools/earnings.js +0 -126
  202. package/tools/friends.js +0 -207
  203. package/tools/genesis.js +0 -233
  204. package/tools/gig-browse.js +0 -206
  205. package/tools/gig-complete.js +0 -144
  206. package/tools/health.js +0 -87
  207. package/tools/leaderboard.js +0 -117
  208. package/tools/lib/git-apply.js +0 -206
  209. package/tools/lib/git-bundle.js +0 -407
  210. package/tools/mint.js +0 -377
  211. package/tools/plan.js +0 -225
  212. package/tools/profile.js +0 -219
  213. package/tools/proof-of-work.js +0 -144
  214. package/tools/pulse.js +0 -218
  215. package/tools/reply.js +0 -166
  216. package/tools/reputation.js +0 -175
  217. package/tools/schedule.js +0 -367
  218. package/tools/search-messages.js +0 -123
  219. package/tools/session.js +0 -467
  220. package/tools/session_price.js +0 -128
  221. package/tools/smart-check.js +0 -201
  222. package/tools/social-processor.js +0 -445
  223. package/tools/streak.js +0 -147
  224. package/tools/stuck.js +0 -297
  225. package/tools/subscribe.js +0 -148
  226. package/tools/subscriptions.js +0 -134
  227. package/tools/tip.js +0 -193
  228. package/tools/wallet.js +0 -269
  229. package/tools/webhook-test.js +0 -388
  230. package/tools/withdraw.js +0 -145
  231. package/tools/work-summary.js +0 -96
  232. package/tools/workshop.js +0 -327
  233. /package/tools/{l2-bridge.js → _experimental/l2-bridge.js} +0 -0
  234. /package/tools/{l2.js → _experimental/l2.js} +0 -0
  235. /package/tools/{_deprecated/away.js → away.js} +0 -0
package/games/drawing.js DELETED
@@ -1,347 +0,0 @@
1
- /**
2
- * Collaborative Drawing game implementation for /vibe
3
- * A shared canvas where multiple users can draw together in real-time
4
- * Create art, doodles, or play drawing games like Pictionary!
5
- */
6
-
7
- // Drawing canvas dimensions (character-based art)
8
- const CANVAS_WIDTH = 20;
9
- const CANVAS_HEIGHT = 12;
10
-
11
- // Drawing tools and colors (using Unicode characters)
12
- const DRAWING_CHARS = {
13
- empty: '⬜', // Empty space
14
- dot: '⚫', // Small dot
15
- circle: '⚪', // Circle
16
- square: '⬛', // Filled square
17
- star: '⭐', // Star
18
- heart: '❤️', // Heart
19
- tree: '🌲', // Tree
20
- house: '🏠', // House
21
- sun: '☀️', // Sun
22
- moon: '🌙', // Moon
23
- water: '🌊', // Wave
24
- mountain: '⛰️', // Mountain
25
- person: '🧍', // Person
26
- cat: '🐱', // Cat
27
- dog: '🐕', // Dog
28
- car: '🚗', // Car
29
- plane: '✈️', // Plane
30
- flower: '🌸', // Flower
31
- umbrella: '☂️', // Umbrella
32
- rainbow: '🌈' // Rainbow
33
- };
34
-
35
- // Create initial drawing game state
36
- function createInitialDrawingState() {
37
- // Initialize empty canvas
38
- const canvas = Array(CANVAS_HEIGHT).fill(null).map(() =>
39
- Array(CANVAS_WIDTH).fill(DRAWING_CHARS.empty)
40
- );
41
-
42
- return {
43
- canvas: canvas,
44
- players: [],
45
- moves: [],
46
- maxPlayers: 8,
47
- gameOver: false,
48
- createdAt: new Date().toISOString(),
49
- lastActivity: new Date().toISOString(),
50
- theme: null, // Optional drawing theme/prompt
51
- mode: 'freeform' // 'freeform', 'pictionary', 'collaborative'
52
- };
53
- }
54
-
55
- // Add player to drawing session
56
- function addPlayer(gameState, playerHandle) {
57
- if (gameState.players.includes(playerHandle)) {
58
- return { error: 'Player already in the drawing session' };
59
- }
60
-
61
- if (gameState.players.length >= gameState.maxPlayers) {
62
- return { error: `Drawing session is full (max ${gameState.maxPlayers} players)` };
63
- }
64
-
65
- const newPlayers = [...gameState.players, playerHandle];
66
-
67
- return {
68
- success: true,
69
- gameState: {
70
- ...gameState,
71
- players: newPlayers,
72
- lastActivity: new Date().toISOString()
73
- }
74
- };
75
- }
76
-
77
- // Make a drawing move (place character at position)
78
- function makeMove(gameState, x, y, char, playerHandle) {
79
- const { canvas, players, moves } = gameState;
80
-
81
- // Validate player
82
- if (!players.includes(playerHandle)) {
83
- return { error: 'You need to join the drawing session first!' };
84
- }
85
-
86
- // Validate position
87
- if (x < 0 || x >= CANVAS_WIDTH || y < 0 || y >= CANVAS_HEIGHT) {
88
- return { error: `Position out of bounds. Canvas is ${CANVAS_WIDTH}x${CANVAS_HEIGHT}` };
89
- }
90
-
91
- // Validate character
92
- const validChars = Object.values(DRAWING_CHARS);
93
- if (!validChars.includes(char)) {
94
- return { error: `Invalid character. Use one of: ${Object.keys(DRAWING_CHARS).join(', ')}` };
95
- }
96
-
97
- // Update canvas
98
- const newCanvas = canvas.map(row => [...row]);
99
- newCanvas[y][x] = char;
100
-
101
- // Record the move
102
- const move = {
103
- x, y, char,
104
- player: playerHandle,
105
- timestamp: new Date().toISOString(),
106
- moveNumber: moves.length + 1
107
- };
108
-
109
- const newMoves = [...moves, move];
110
-
111
- const newGameState = {
112
- ...gameState,
113
- canvas: newCanvas,
114
- moves: newMoves,
115
- lastActivity: new Date().toISOString()
116
- };
117
-
118
- return { success: true, gameState: newGameState };
119
- }
120
-
121
- // Draw a line between two points (simple Bresenham-like algorithm)
122
- function drawLine(gameState, x0, y0, x1, y1, char, playerHandle) {
123
- const moves = [];
124
-
125
- // Simple line drawing - just plot points between start and end
126
- const dx = Math.abs(x1 - x0);
127
- const dy = Math.abs(y1 - y0);
128
- const steps = Math.max(dx, dy);
129
-
130
- if (steps === 0) {
131
- // Single point
132
- const result = makeMove(gameState, x0, y0, char, playerHandle);
133
- return result;
134
- }
135
-
136
- const xInc = (x1 - x0) / steps;
137
- const yInc = (y1 - y0) / steps;
138
-
139
- let currentGameState = gameState;
140
-
141
- for (let i = 0; i <= steps; i++) {
142
- const x = Math.round(x0 + i * xInc);
143
- const y = Math.round(y0 + i * yInc);
144
-
145
- const result = makeMove(currentGameState, x, y, char, playerHandle);
146
- if (result.error) {
147
- // Stop on first error but return what we accomplished
148
- break;
149
- }
150
- currentGameState = result.gameState;
151
- }
152
-
153
- return { success: true, gameState: currentGameState };
154
- }
155
-
156
- // Clear a region of the canvas
157
- function clearRegion(gameState, x0, y0, x1, y1, playerHandle) {
158
- const { players } = gameState;
159
-
160
- if (!players.includes(playerHandle)) {
161
- return { error: 'You need to join the drawing session first!' };
162
- }
163
-
164
- // Ensure coordinates are in bounds and properly ordered
165
- const minX = Math.max(0, Math.min(x0, x1));
166
- const maxX = Math.min(CANVAS_WIDTH - 1, Math.max(x0, x1));
167
- const minY = Math.max(0, Math.min(y0, y1));
168
- const maxY = Math.min(CANVAS_HEIGHT - 1, Math.max(y0, y1));
169
-
170
- const newCanvas = gameState.canvas.map(row => [...row]);
171
-
172
- for (let y = minY; y <= maxY; y++) {
173
- for (let x = minX; x <= maxX; x++) {
174
- newCanvas[y][x] = DRAWING_CHARS.empty;
175
- }
176
- }
177
-
178
- const clearMove = {
179
- action: 'clear',
180
- x0: minX, y0: minY, x1: maxX, y1: maxY,
181
- player: playerHandle,
182
- timestamp: new Date().toISOString(),
183
- moveNumber: gameState.moves.length + 1
184
- };
185
-
186
- return {
187
- success: true,
188
- gameState: {
189
- ...gameState,
190
- canvas: newCanvas,
191
- moves: [...gameState.moves, clearMove],
192
- lastActivity: new Date().toISOString()
193
- }
194
- };
195
- }
196
-
197
- // Format drawing canvas for display
198
- function formatDrawingDisplay(gameState) {
199
- const { canvas, players, moves, theme, mode } = gameState;
200
-
201
- let display = `🎨 **Collaborative Drawing** (${players.length} artist${players.length !== 1 ? 's' : ''})\n\n`;
202
-
203
- // Show theme if set
204
- if (theme) {
205
- display += `🎯 **Theme:** ${theme}\n\n`;
206
- }
207
-
208
- // Show canvas with coordinate labels
209
- display += '```\n';
210
-
211
- // Top coordinate labels
212
- let topLabels = ' ';
213
- for (let x = 0; x < CANVAS_WIDTH; x++) {
214
- if (x < 10) {
215
- topLabels += x + ' ';
216
- } else {
217
- topLabels += String.fromCharCode(55 + x); // A, B, C... for 10+
218
- }
219
- }
220
- display += topLabels + '\n';
221
-
222
- // Canvas rows with left coordinate labels
223
- for (let y = 0; y < CANVAS_HEIGHT; y++) {
224
- let row = (y < 10 ? ' ' + y : String.fromCharCode(55 + y)) + ' ';
225
- row += canvas[y].join('');
226
- display += row + '\n';
227
- }
228
-
229
- display += '```\n\n';
230
-
231
- // Show players
232
- if (players.length > 0) {
233
- display += `**Artists:** ${players.map(p => `@${p}`).join(', ')}\n\n`;
234
- }
235
-
236
- // Show recent activity
237
- if (moves.length > 0) {
238
- const recentMoves = moves.slice(-3); // Last 3 moves
239
- display += '**Recent activity:**\n';
240
- for (const move of recentMoves) {
241
- if (move.action === 'clear') {
242
- display += `• @${move.player} cleared region (${move.x0},${move.y0}) to (${move.x1},${move.y1})\n`;
243
- } else {
244
- display += `• @${move.player} drew ${move.char} at (${move.x},${move.y})\n`;
245
- }
246
- }
247
- display += '\n';
248
- }
249
-
250
- // Show available characters
251
- display += '**Available characters:**\n';
252
- const charEntries = Object.entries(DRAWING_CHARS);
253
- const charGroups = [];
254
- for (let i = 0; i < charEntries.length; i += 5) {
255
- const group = charEntries.slice(i, i + 5);
256
- charGroups.push(group.map(([name, char]) => `${char} (${name})`).join(' '));
257
- }
258
- display += charGroups.join('\n') + '\n\n';
259
-
260
- return display;
261
- }
262
-
263
- // Set drawing theme/prompt
264
- function setTheme(gameState, theme, playerHandle) {
265
- const { players } = gameState;
266
-
267
- if (!players.includes(playerHandle)) {
268
- return { error: 'You need to join the drawing session first!' };
269
- }
270
-
271
- return {
272
- success: true,
273
- gameState: {
274
- ...gameState,
275
- theme: theme,
276
- lastActivity: new Date().toISOString()
277
- }
278
- };
279
- }
280
-
281
- // Generate drawing tips based on theme
282
- function getDrawingTips(theme) {
283
- const tips = {
284
- 'house': ['Start with a ⬛ base', 'Add a roof with ⛰️', 'Use 🏠 for details'],
285
- 'landscape': ['Use 🌲 for trees', '☀️ for sun', '🌊 for water', '⛰️ for mountains'],
286
- 'portrait': ['Use 🧍 for people', '⚪⚫ for eyes', '❤️ for heart'],
287
- 'animals': ['Try 🐱🐕 for pets', '🌲 for habitat', '⭐ for magical touches'],
288
- 'vehicle': ['🚗 for cars', '✈️ for planes', '⬛ for roads'],
289
- 'nature': ['🌸 for flowers', '🌲 for trees', '☀️🌙 for sky', '🌈 for color']
290
- };
291
-
292
- const defaultTips = ['Use ⬛⬜ for shapes', 'Add ⭐❤️ for details', 'Try 🌸🌲 for nature'];
293
-
294
- return tips[theme?.toLowerCase()] || defaultTips;
295
- }
296
-
297
- // Get canvas statistics
298
- function getCanvasStats(gameState) {
299
- const { canvas, moves, players } = gameState;
300
-
301
- // Count characters used
302
- const charCount = {};
303
- for (const row of canvas) {
304
- for (const char of row) {
305
- if (char !== DRAWING_CHARS.empty) {
306
- charCount[char] = (charCount[char] || 0) + 1;
307
- }
308
- }
309
- }
310
-
311
- // Count moves per player
312
- const playerMoves = {};
313
- for (const move of moves) {
314
- if (move.player) {
315
- playerMoves[move.player] = (playerMoves[move.player] || 0) + 1;
316
- }
317
- }
318
-
319
- const totalDrawnCells = Object.values(charCount).reduce((a, b) => a + b, 0);
320
- const totalCells = CANVAS_WIDTH * CANVAS_HEIGHT;
321
- const fillPercentage = Math.round((totalDrawnCells / totalCells) * 100);
322
-
323
- return {
324
- totalMoves: moves.length,
325
- totalDrawnCells,
326
- fillPercentage,
327
- uniqueCharsUsed: Object.keys(charCount).length,
328
- charCount,
329
- playerMoves,
330
- mostUsedChar: Object.entries(charCount).sort(([,a], [,b]) => b - a)[0]
331
- };
332
- }
333
-
334
- module.exports = {
335
- createInitialDrawingState,
336
- addPlayer,
337
- makeMove,
338
- drawLine,
339
- clearRegion,
340
- formatDrawingDisplay,
341
- setTheme,
342
- getDrawingTips,
343
- getCanvasStats,
344
- DRAWING_CHARS,
345
- CANVAS_WIDTH,
346
- CANVAS_HEIGHT
347
- };
@@ -1,300 +0,0 @@
1
- /**
2
- * Game Roulette - Discover random games from the /vibe workshop
3
- * Perfect for when you want to play something but don't know what!
4
- */
5
-
6
- const arcade = require('./arcade');
7
-
8
- // Game difficulty weights for smart recommendations
9
- const DIFFICULTY_WEIGHTS = {
10
- 'Easy': 0.4,
11
- 'Medium': 0.4,
12
- 'Hard': 0.2
13
- };
14
-
15
- // Player count weights for recommendations
16
- const PLAYER_WEIGHTS = {
17
- 'Solo': 0.3,
18
- '1v1': 0.4,
19
- '1v2': 0.1,
20
- 'Multiplayer': 0.2
21
- };
22
-
23
- // Create initial game roulette state
24
- function createInitialGameRouletteState() {
25
- return {
26
- lastRecommendation: null,
27
- userPreferences: {},
28
- playHistory: [],
29
- sessionStarted: new Date().toISOString(),
30
- totalRecommendations: 0
31
- };
32
- }
33
-
34
- // Get a random game recommendation
35
- function getRandomGame(gameState, userHandle = null, preferences = {}) {
36
- const { GAMES } = arcade;
37
- const gameIds = Object.keys(GAMES);
38
-
39
- // Apply preferences if provided
40
- let filteredGames = gameIds;
41
-
42
- if (preferences.difficulty) {
43
- filteredGames = filteredGames.filter(id =>
44
- GAMES[id].difficulty.toLowerCase() === preferences.difficulty.toLowerCase()
45
- );
46
- }
47
-
48
- if (preferences.category) {
49
- filteredGames = filteredGames.filter(id =>
50
- GAMES[id].category === preferences.category
51
- );
52
- }
53
-
54
- if (preferences.players) {
55
- filteredGames = filteredGames.filter(id =>
56
- GAMES[id].players === preferences.players
57
- );
58
- }
59
-
60
- // Avoid recommending the same game twice in a row
61
- if (gameState.lastRecommendation && filteredGames.length > 1) {
62
- filteredGames = filteredGames.filter(id => id !== gameState.lastRecommendation.id);
63
- }
64
-
65
- if (filteredGames.length === 0) {
66
- return { error: 'No games match your preferences!' };
67
- }
68
-
69
- // Pick random game
70
- const randomId = filteredGames[Math.floor(Math.random() * filteredGames.length)];
71
- const game = { id: randomId, ...GAMES[randomId] };
72
-
73
- // Update game state
74
- const newGameState = {
75
- ...gameState,
76
- lastRecommendation: game,
77
- totalRecommendations: gameState.totalRecommendations + 1,
78
- playHistory: [...gameState.playHistory.slice(-9), game] // Keep last 10
79
- };
80
-
81
- if (userHandle) {
82
- // Track user preferences
83
- newGameState.userPreferences[userHandle] = {
84
- ...gameState.userPreferences[userHandle],
85
- lastSeen: new Date().toISOString(),
86
- totalRequests: (gameState.userPreferences[userHandle]?.totalRequests || 0) + 1
87
- };
88
- }
89
-
90
- return { success: true, gameState: newGameState, recommendation: game };
91
- }
92
-
93
- // Get smart recommendation based on user history
94
- function getSmartRecommendation(gameState, userHandle, mood = null) {
95
- const { GAMES, CATEGORIES } = arcade;
96
-
97
- // Mood-based filtering
98
- const moodCategories = {
99
- 'chill': ['word', 'puzzle'],
100
- 'competitive': ['classic', 'action'],
101
- 'social': ['social', 'creative'],
102
- 'quick': ['classic', 'action'],
103
- 'thoughtful': ['puzzle', 'word'],
104
- 'creative': ['creative', 'social']
105
- };
106
-
107
- const preferences = {};
108
- if (mood && moodCategories[mood]) {
109
- preferences.categoryList = moodCategories[mood];
110
- }
111
-
112
- return getRecommendationWithFilters(gameState, userHandle, preferences);
113
- }
114
-
115
- // Get recommendation with advanced filters
116
- function getRecommendationWithFilters(gameState, userHandle, filters = {}) {
117
- const { GAMES } = arcade;
118
- let candidateGames = Object.keys(GAMES);
119
-
120
- // Apply category list filter
121
- if (filters.categoryList) {
122
- candidateGames = candidateGames.filter(id =>
123
- filters.categoryList.includes(GAMES[id].category)
124
- );
125
- }
126
-
127
- // Apply other filters
128
- if (filters.difficulty) {
129
- candidateGames = candidateGames.filter(id =>
130
- GAMES[id].difficulty.toLowerCase() === filters.difficulty.toLowerCase()
131
- );
132
- }
133
-
134
- if (filters.maxPlayers && filters.maxPlayers < 4) {
135
- candidateGames = candidateGames.filter(id =>
136
- GAMES[id].players === 'Solo' || GAMES[id].players === '1v1'
137
- );
138
- }
139
-
140
- // Weighted random selection
141
- if (candidateGames.length > 1) {
142
- const weights = candidateGames.map(id => {
143
- const game = GAMES[id];
144
- let weight = 1.0;
145
-
146
- // Prefer easier games slightly
147
- if (game.difficulty === 'Easy') weight *= 1.2;
148
- else if (game.difficulty === 'Hard') weight *= 0.8;
149
-
150
- // Prefer variety - reduce weight if recently recommended
151
- const recentHistory = gameState.playHistory.slice(-3);
152
- if (recentHistory.some(h => h.id === id)) {
153
- weight *= 0.3;
154
- }
155
-
156
- return weight;
157
- });
158
-
159
- // Weighted random selection
160
- const totalWeight = weights.reduce((a, b) => a + b, 0);
161
- let random = Math.random() * totalWeight;
162
-
163
- for (let i = 0; i < candidateGames.length; i++) {
164
- random -= weights[i];
165
- if (random <= 0) {
166
- const gameId = candidateGames[i];
167
- const game = { id: gameId, ...GAMES[gameId] };
168
-
169
- const newGameState = {
170
- ...gameState,
171
- lastRecommendation: game,
172
- totalRecommendations: gameState.totalRecommendations + 1,
173
- playHistory: [...gameState.playHistory.slice(-9), game]
174
- };
175
-
176
- return { success: true, gameState: newGameState, recommendation: game };
177
- }
178
- }
179
- }
180
-
181
- // Fallback to simple random
182
- return getRandomGame(gameState, userHandle, filters);
183
- }
184
-
185
- // Get games by current "vibe"
186
- function getGamesByVibe(vibe) {
187
- const { GAMES } = arcade;
188
- const vibes = {
189
- 'competitive': ['chess', 'tictactoe', 'multiplayer-tictactoe', 'quickduel', 'rockpaperscissors'],
190
- 'social': ['storybuilder', 'drawing', 'twotruths', 'werewolf', 'wordassociation'],
191
- 'solo': ['snake', 'memory', 'hangman', 'riddle', 'guessnumber', 'colorguess'],
192
- 'quick': ['rockpaperscissors', 'guessnumber', 'colorguess', 'quickduel'],
193
- 'creative': ['drawing', 'storybuilder'],
194
- 'thinking': ['chess', 'riddle', 'twentyquestions', 'hangman'],
195
- 'party': ['werewolf', 'twotruths', 'wordassociation', 'drawing']
196
- };
197
-
198
- const gameIds = vibes[vibe.toLowerCase()] || [];
199
- return gameIds.map(id => ({ id, ...GAMES[id] })).filter(g => g.name);
200
- }
201
-
202
- // Format roulette display
203
- function formatRouletteDisplay(gameState, recommendation, userHandle = null) {
204
- if (!recommendation) {
205
- return 'Game Roulette ready! Spin the wheel to discover your next game!';
206
- }
207
-
208
- const { icon, name, description, category, players, difficulty } = recommendation;
209
-
210
- let display = `🎲 **GAME ROULETTE** 🎲\n\n`;
211
- display += `${icon} **${name}**\n\n`;
212
- display += `**Description:** ${description}\n`;
213
- display += `**Category:** ${category.charAt(0).toUpperCase() + category.slice(1)}\n`;
214
- display += `**Players:** ${players}\n`;
215
- display += `**Difficulty:** ${difficulty}\n\n`;
216
-
217
- // Add contextual launch instructions
218
- if (['tictactoe', 'chess'].includes(recommendation.id)) {
219
- display += `**How to play:** \`vibe game @username\` to challenge someone!\n`;
220
- } else if (recommendation.id === 'drawing') {
221
- display += `**How to play:** Join the collaborative canvas and start drawing!\n`;
222
- } else if (recommendation.id === 'arcade') {
223
- display += `**How to play:** Browse all games in the Workshop Arcade!\n`;
224
- } else {
225
- display += `**How to play:** Launch ${name} and dive in!\n`;
226
- }
227
-
228
- display += `\n🎯 **Feeling lucky?** Spin again for another recommendation!`;
229
-
230
- if (gameState.totalRecommendations > 1) {
231
- display += `\n\n*This is recommendation #${gameState.totalRecommendations} in your session*`;
232
- }
233
-
234
- return display;
235
- }
236
-
237
- // Get roulette statistics
238
- function getRouletteStats(gameState) {
239
- const { playHistory, totalRecommendations } = gameState;
240
-
241
- if (playHistory.length === 0) {
242
- return null;
243
- }
244
-
245
- // Category distribution
246
- const categoryCount = {};
247
- const difficultyCount = {};
248
- const playerCount = {};
249
-
250
- for (const game of playHistory) {
251
- categoryCount[game.category] = (categoryCount[game.category] || 0) + 1;
252
- difficultyCount[game.difficulty] = (difficultyCount[game.difficulty] || 0) + 1;
253
- playerCount[game.players] = (playerCount[game.players] || 0) + 1;
254
- }
255
-
256
- const mostRecommendedCategory = Object.entries(categoryCount)
257
- .sort(([,a], [,b]) => b - a)[0];
258
-
259
- const mostRecommendedDifficulty = Object.entries(difficultyCount)
260
- .sort(([,a], [,b]) => b - a)[0];
261
-
262
- return {
263
- totalRecommendations,
264
- sessionGames: playHistory.length,
265
- favoriteCategory: mostRecommendedCategory ? mostRecommendedCategory[0] : null,
266
- favoriteDifficulty: mostRecommendedDifficulty ? mostRecommendedDifficulty[0] : null,
267
- categoryDistribution: categoryCount,
268
- difficultyDistribution: difficultyCount,
269
- playerDistribution: playerCount
270
- };
271
- }
272
-
273
- // Generate fun recommendation messages
274
- function getRandomRouletteMessage() {
275
- const messages = [
276
- "🎲 The roulette wheel is spinning...",
277
- "🎯 Searching for your perfect game match...",
278
- "🎰 Rolling the dice of destiny...",
279
- "🔮 Consulting the gaming crystal ball...",
280
- "🎪 Welcome to the game carnival!",
281
- "🚀 Launching game discovery sequence...",
282
- "🎨 Painting your gaming adventure...",
283
- "⚡ Generating gaming lightning in a bottle...",
284
- "🧩 Assembling your perfect game puzzle...",
285
- "🌟 Aligning the gaming stars for you..."
286
- ];
287
-
288
- return messages[Math.floor(Math.random() * messages.length)];
289
- }
290
-
291
- module.exports = {
292
- createInitialGameRouletteState,
293
- getRandomGame,
294
- getSmartRecommendation,
295
- getRecommendationWithFilters,
296
- getGamesByVibe,
297
- formatRouletteDisplay,
298
- getRouletteStats,
299
- getRandomRouletteMessage
300
- };