slashvibe-mcp 0.3.21 → 0.3.22

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 (229) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +280 -47
  3. package/config.js +36 -31
  4. package/crypto.js +1 -6
  5. package/discord.js +19 -19
  6. package/index.js +217 -207
  7. package/intelligence/index.js +2 -9
  8. package/intelligence/infer.js +10 -16
  9. package/intelligence/patterns.js +23 -18
  10. package/intelligence/proactive.js +16 -15
  11. package/intelligence/serendipity.js +57 -20
  12. package/memory.js +13 -8
  13. package/notify.js +39 -14
  14. package/package.json +27 -20
  15. package/presence.js +2 -2
  16. package/prompts.js +5 -9
  17. package/protocol/index.js +123 -87
  18. package/protocol/telegram-commands.js +36 -37
  19. package/store/api.js +358 -529
  20. package/store/local.js +9 -10
  21. package/store/profiles.js +48 -192
  22. package/store/reservations.js +2 -9
  23. package/store/skills.js +69 -71
  24. package/store/sqlite.js +355 -0
  25. package/tools/_actions.js +48 -387
  26. package/tools/_connection-queue.js +45 -56
  27. package/tools/_discovery-enhanced.js +52 -57
  28. package/tools/_discovery.js +87 -185
  29. package/tools/{l2-status.js → _experimental/l2-status.js} +68 -70
  30. package/tools/{shipback.js → _experimental/shipback.js} +4 -3
  31. package/tools/_proactive-discovery.js +60 -73
  32. package/tools/_shared/index.js +41 -64
  33. package/tools/admin-inbox.js +10 -15
  34. package/tools/agents.js +1 -1
  35. package/tools/artifact-create.js +13 -23
  36. package/tools/artifact-view.js +4 -4
  37. package/tools/{_deprecated/back.js → back.js} +1 -1
  38. package/tools/bye.js +3 -5
  39. package/tools/consent.js +2 -2
  40. package/tools/context.js +9 -10
  41. package/tools/crossword.js +3 -2
  42. package/tools/discover.js +94 -356
  43. package/tools/dm.js +27 -86
  44. package/tools/doctor.js +12 -41
  45. package/tools/drawing.js +34 -20
  46. package/tools/echo.js +11 -11
  47. package/tools/feed.js +30 -58
  48. package/tools/follow.js +64 -187
  49. package/tools/{_deprecated/forget.js → forget.js} +4 -7
  50. package/tools/game.js +144 -48
  51. package/tools/handoff.js +6 -8
  52. package/tools/help.js +3 -3
  53. package/tools/idea.js +15 -27
  54. package/tools/inbox.js +121 -293
  55. package/tools/init.js +54 -151
  56. package/tools/invite.js +8 -21
  57. package/tools/migrate.js +27 -24
  58. package/tools/multiplayer-game.js +50 -40
  59. package/tools/{_deprecated/mute.js → mute.js} +4 -3
  60. package/tools/notifications.js +58 -48
  61. package/tools/observe.js +12 -15
  62. package/tools/onboarding.js +8 -11
  63. package/tools/open.js +13 -144
  64. package/tools/party-game.js +23 -12
  65. package/tools/patterns.js +2 -1
  66. package/tools/ping.js +5 -7
  67. package/tools/react.js +28 -30
  68. package/tools/{_deprecated/recall.js → recall.js} +5 -10
  69. package/tools/release.js +4 -2
  70. package/tools/{_deprecated/remember.js → remember.js} +4 -6
  71. package/tools/report.js +2 -2
  72. package/tools/request.js +6 -26
  73. package/tools/reserve.js +1 -1
  74. package/tools/session-fork.js +97 -0
  75. package/tools/session-save.js +109 -0
  76. package/tools/settings.js +30 -99
  77. package/tools/ship.js +74 -56
  78. package/tools/{_deprecated/skills-exchange.js → skills-exchange.js} +38 -39
  79. package/tools/social-inbox.js +22 -28
  80. package/tools/social-post.js +24 -27
  81. package/tools/solo-game.js +54 -46
  82. package/tools/start.js +14 -148
  83. package/tools/status.js +21 -68
  84. package/tools/submit.js +4 -2
  85. package/tools/suggest-tags.js +36 -33
  86. package/tools/summarize.js +19 -16
  87. package/tools/tag-suggestions.js +72 -73
  88. package/tools/test.js +1 -1
  89. package/tools/{_deprecated/tictactoe.js → tictactoe.js} +26 -26
  90. package/tools/token.js +4 -4
  91. package/tools/update.js +1 -2
  92. package/tools/watch.js +132 -112
  93. package/tools/who.js +20 -40
  94. package/tools/{_deprecated/wordassociation.js → wordassociation.js} +23 -20
  95. package/tools/workshop-buddy.js +52 -53
  96. package/tools/x-mentions.js +0 -1
  97. package/tools/x-reply.js +0 -1
  98. package/twitter.js +14 -20
  99. package/version.json +8 -10
  100. package/analytics.js +0 -107
  101. package/auth-store.js +0 -148
  102. package/auto-update.js +0 -130
  103. package/bridges/bridge-monitor.js +0 -388
  104. package/bridges/discord-bot.js +0 -431
  105. package/bridges/farcaster.js +0 -299
  106. package/bridges/telegram.js +0 -261
  107. package/bridges/webhook-health.js +0 -420
  108. package/bridges/webhook-server.js +0 -437
  109. package/bridges/whatsapp.js +0 -441
  110. package/bridges/x-webhook.js +0 -423
  111. package/games/arcade.js +0 -406
  112. package/games/chess.js +0 -451
  113. package/games/colorguess.js +0 -343
  114. package/games/crossword-words.js +0 -171
  115. package/games/crossword.js +0 -461
  116. package/games/drawing.js +0 -347
  117. package/games/gameroulette.js +0 -300
  118. package/games/gamerouter.js +0 -336
  119. package/games/gamestatus.js +0 -337
  120. package/games/guessnumber.js +0 -209
  121. package/games/hangman.js +0 -279
  122. package/games/memory.js +0 -338
  123. package/games/multiplayer-tictactoe.js +0 -389
  124. package/games/pixelart.js +0 -399
  125. package/games/quickduel.js +0 -354
  126. package/games/riddle.js +0 -371
  127. package/games/rockpaperscissors.js +0 -291
  128. package/games/snake.js +0 -406
  129. package/games/storybuilder.js +0 -343
  130. package/games/tictactoe.js +0 -345
  131. package/games/twentyquestions.js +0 -286
  132. package/games/twotruths.js +0 -207
  133. package/games/werewolf.js +0 -508
  134. package/games/wordassociation.js +0 -247
  135. package/games/wordchain.js +0 -135
  136. package/intelligence/interests.js +0 -369
  137. package/notification-emitter.js +0 -77
  138. package/setup.js +0 -480
  139. package/smart-inbox.js +0 -276
  140. package/tools/_deprecated/auto-suggest-connections.js +0 -304
  141. package/tools/_deprecated/bootstrap-skills.js +0 -231
  142. package/tools/_deprecated/bridge-dashboard.js +0 -342
  143. package/tools/_deprecated/bridge-health.js +0 -400
  144. package/tools/_deprecated/bridge-live.js +0 -384
  145. package/tools/_deprecated/bridges.js +0 -383
  146. package/tools/_deprecated/colorguess.js +0 -281
  147. package/tools/_deprecated/discover-insights.js +0 -379
  148. package/tools/_deprecated/discover-momentum.js +0 -256
  149. package/tools/_deprecated/discovery-analytics.js +0 -345
  150. package/tools/_deprecated/discovery-auto-suggest.js +0 -275
  151. package/tools/_deprecated/discovery-bootstrap.js +0 -267
  152. package/tools/_deprecated/discovery-daily.js +0 -375
  153. package/tools/_deprecated/discovery-dashboard.js +0 -385
  154. package/tools/_deprecated/discovery-digest.js +0 -314
  155. package/tools/_deprecated/discovery-hub.js +0 -357
  156. package/tools/_deprecated/discovery-insights.js +0 -384
  157. package/tools/_deprecated/discovery-momentum.js +0 -281
  158. package/tools/_deprecated/discovery-monitor.js +0 -319
  159. package/tools/_deprecated/discovery-proactive.js +0 -300
  160. package/tools/_deprecated/draw.js +0 -317
  161. package/tools/_deprecated/farcaster.js +0 -307
  162. package/tools/_deprecated/games-catalog.js +0 -376
  163. package/tools/_deprecated/games.js +0 -313
  164. package/tools/_deprecated/guessnumber.js +0 -194
  165. package/tools/_deprecated/hangman.js +0 -129
  166. package/tools/_deprecated/multiplayer-tictactoe.js +0 -303
  167. package/tools/_deprecated/riddle.js +0 -240
  168. package/tools/_deprecated/run-bootstrap.js +0 -69
  169. package/tools/_deprecated/skills-analytics.js +0 -349
  170. package/tools/_deprecated/skills-bootstrap.js +0 -301
  171. package/tools/_deprecated/skills-dashboard.js +0 -268
  172. package/tools/_deprecated/skills.js +0 -380
  173. package/tools/_deprecated/smart-intro.js +0 -353
  174. package/tools/_deprecated/storybuilder.js +0 -331
  175. package/tools/_deprecated/telegram-bot.js +0 -183
  176. package/tools/_deprecated/telegram-setup.js +0 -214
  177. package/tools/_deprecated/twentyquestions.js +0 -143
  178. package/tools/_shared.js +0 -234
  179. package/tools/_work-context.js +0 -338
  180. package/tools/_work-context.manual-test.js +0 -199
  181. package/tools/_work-context.test.js +0 -260
  182. package/tools/activity.js +0 -220
  183. package/tools/agent-treasury.js +0 -288
  184. package/tools/analytics.js +0 -191
  185. package/tools/approve.js +0 -197
  186. package/tools/arcade.js +0 -173
  187. package/tools/artifacts-price.js +0 -107
  188. package/tools/ask-expert.js +0 -160
  189. package/tools/available.js +0 -120
  190. package/tools/become-expert.js +0 -150
  191. package/tools/broadcast.js +0 -325
  192. package/tools/chat.js +0 -202
  193. package/tools/collaborative-drawing.js +0 -286
  194. package/tools/connection-status.js +0 -178
  195. package/tools/earnings.js +0 -126
  196. package/tools/friends.js +0 -207
  197. package/tools/genesis.js +0 -233
  198. package/tools/gig-browse.js +0 -206
  199. package/tools/gig-complete.js +0 -144
  200. package/tools/health.js +0 -87
  201. package/tools/leaderboard.js +0 -117
  202. package/tools/lib/git-apply.js +0 -206
  203. package/tools/lib/git-bundle.js +0 -407
  204. package/tools/mint.js +0 -377
  205. package/tools/plan.js +0 -225
  206. package/tools/profile.js +0 -219
  207. package/tools/proof-of-work.js +0 -144
  208. package/tools/pulse.js +0 -218
  209. package/tools/reply.js +0 -166
  210. package/tools/reputation.js +0 -175
  211. package/tools/schedule.js +0 -367
  212. package/tools/search-messages.js +0 -123
  213. package/tools/session.js +0 -467
  214. package/tools/session_price.js +0 -128
  215. package/tools/smart-check.js +0 -201
  216. package/tools/social-processor.js +0 -445
  217. package/tools/streak.js +0 -147
  218. package/tools/stuck.js +0 -297
  219. package/tools/subscribe.js +0 -148
  220. package/tools/subscriptions.js +0 -134
  221. package/tools/tip.js +0 -193
  222. package/tools/wallet.js +0 -269
  223. package/tools/webhook-test.js +0 -388
  224. package/tools/withdraw.js +0 -145
  225. package/tools/work-summary.js +0 -96
  226. package/tools/workshop.js +0 -327
  227. /package/tools/{l2-bridge.js → _experimental/l2-bridge.js} +0 -0
  228. /package/tools/{l2.js → _experimental/l2.js} +0 -0
  229. /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
- };