ishumdz-bail 1.0.11 → 1.0.13
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.
- package/README.md +95 -46
- package/lib/Games/chess.js +92 -0
- package/lib/Games/connect4.js +114 -0
- package/lib/Games/game2048.js +99 -0
- package/lib/Games/htmlGame.js +89 -0
- package/lib/Games/index.js +31 -1
- package/lib/Games/memory.js +82 -0
- package/lib/Games/snake.js +91 -0
- package/lib/Games/tictactoe.js +62 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,47 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
```
|
|
3
|
-
╔═══════════════════════════════════════════════════════════════════════════╗
|
|
4
|
-
║ 📱 MAIN FEATURES ║
|
|
5
|
-
╠═══════════════════════════════════════════════════════════════════════════╣
|
|
6
|
-
║ ║
|
|
7
|
-
║ ┌─────────────────────────────────────────────────────────────────────┐ ║
|
|
8
|
-
║ │ 🎮 GAMES │ 📞 VOIP CALLER │ ⚡ PRO METHODS │ ║
|
|
9
|
-
║ │ ├─ Blackjack │ ├─ Auto Answer │ ├─ sendPoll │ ║
|
|
10
|
-
║ │ ├─ Slots │ ├─ Audio Playback │ ├─ sendPollVote │ ║
|
|
11
|
-
║ │ ├─ Dice │ └─ VoipClient │ ├─ editMessage │ ║
|
|
12
|
-
║ │ ├─ Coin Flip │ │ ├─ deleteMessage │ ║
|
|
13
|
-
║ │ ├─ Roulette │ 📊 POLLS │ ├─ pinMessage │ ║
|
|
14
|
-
║ │ ├─ Mines │ ├─ Create Poll │ ├─ starMessage │ ║
|
|
15
|
-
║ │ ├─ Trivia │ ├─ Vote Poll │ ├─ reactMessage │ ║
|
|
16
|
-
║ │ └─ RPS │ └─ Get Results │ ├─ sendPresence │ ║
|
|
17
|
-
║ └─────────────────────────────────────────────────────────────────────┘ ║
|
|
18
|
-
║ ║
|
|
19
|
-
║ ┌─────────────────────────────────────────────────────────────────────┐ ║
|
|
20
|
-
║ │ 📢 NEWSLETTER │ 🟢 STATUS │ 💬 CHAT │ ║
|
|
21
|
-
║ │ ├─ channelVote │ ├─ sendText │ ├─ editMessage │ ║
|
|
22
|
-
║ │ ├─ newsletterReact │ ├─ sendMedia │ ├─ deleteMessage │ ║
|
|
23
|
-
║ │ ├─ getMessages │ ├─ readStatus │ ├─ pinMessage │ ║
|
|
24
|
-
║ │ ├─ searchChannels │ └─ reactStatus │ ├─ unpinMessage │ ║
|
|
25
|
-
║ │ └─ listChannels │ │ ├─ starMessage │ ║
|
|
26
|
-
║ └─────────────────────────────────────────────────────────────────────┘ ║
|
|
27
|
-
║ ║
|
|
28
|
-
║ ┌─────────────────────────────────────────────────────────────────────┐ ║
|
|
29
|
-
║ │ 👥 GROUPS │ 👤 PROFILE │ 📰 MEDIA │ ║
|
|
30
|
-
║ │ ├─ Get Invite Info │ ├─ checkNumber │ ├─ Newsletter Paths │ ║
|
|
31
|
-
║ │ ├─ Join via Invite │ ├─ setBio │ ├─ Image Upload │ ║
|
|
32
|
-
║ │ ├─ Set Announcement │ ├─ updateName │ ├─ Video Upload │ ║
|
|
33
|
-
║ │ ├─ Set Locked │ ├─ setProfilePic │ ├─ Audio Upload │ ║
|
|
34
|
-
║ │ ├─ Request List │ ├─ removeProfilePic │ ├─ Document Upload │ ║
|
|
35
|
-
║ │ ├─ Approve │ └─ rejectCall │ └─ Sticker Upload │ ║
|
|
36
|
-
║ │ └─ Reject │ │ │ ║
|
|
37
|
-
║ └─────────────────────────────────────────────────────────────────────┘ ║
|
|
38
|
-
║ ║
|
|
39
|
-
║ ✅ No Browser Required ✅ Multi-Device ✅ WebSocket Based ║
|
|
40
|
-
║ ✅ Active Maintenance ✅ MIT License ✅ Full API Coverage ║
|
|
41
|
-
║ ║
|
|
42
|
-
╚═══════════════════════════════════════════════════════════════════════════╝
|
|
43
|
-
```
|
|
44
|
-
|
|
45
1
|
<div align="center">
|
|
46
2
|
<img src="https://capsule-render.vercel.app/api?type=waving&height=220&color=0:ff6b6b,40:f06595,100:845ef7&text=ishumdz-bail&fontAlignY=40&fontSize=44&fontColor=ffffff&desc=Stable%20WhatsApp%20Web%20API%20Fork%20for%20Production%20Bots&descAlignY=60&descSize=16" alt="Header Banner" />
|
|
47
3
|
|
|
@@ -134,7 +90,7 @@ sock.ev.on('creds.update', saveCreds)
|
|
|
134
90
|
| 📣 **Newsletter/Channel Media Upload** | Full support for uploading images, videos, audio, stickers to channels using correct `/newsletter/newsletter-*` paths |
|
|
135
91
|
| 😀 **Newsletter Reactions** | React to channel messages with emojis, track votes, and manage newsletter engagement |
|
|
136
92
|
| 📊 **Poll Voting** | Create polls, track encrypted votes, display results, and support multiple selection polls |
|
|
137
|
-
| 🎮 **Built-in Games** | 8 games
|
|
93
|
+
| 🎮 **Built-in Games** | 8 text games (Blackjack, Slots, Dice, Coin Flip, Roulette, Mines, Trivia, RPS) + 6 live HTML games (Chess, Tic Tac Toe, Connect Four, 2048, Snake, Memory) playable inside chat |
|
|
138
94
|
| 📞 **VoIP Caller** | Answer incoming calls with audio playback using WASM VoIP engine |
|
|
139
95
|
| ⚡ **Pro Methods** | 34 convenience methods: Polls, Newsletter, Status, Chat, Groups, Profile |
|
|
140
96
|
|
|
@@ -755,6 +711,51 @@ import {
|
|
|
755
711
|
} from 'ishumdz-bail'
|
|
756
712
|
```
|
|
757
713
|
|
|
714
|
+
### 🕹️ Live HTML Games (playable inside chat!)
|
|
715
|
+
|
|
716
|
+
Send fully interactive games that render as **Meta AI style live HTML cards** inside WhatsApp (GenAI HTML primitive) — board, AI opponent, score tracking, all running client-side in the chat window. English UI.
|
|
717
|
+
|
|
718
|
+
```javascript
|
|
719
|
+
import {
|
|
720
|
+
sendChess, // ♚ Chess vs AI (minimax)
|
|
721
|
+
sendTicTacToe, // 🎮 Tic Tac Toe vs AI (unbeatable)
|
|
722
|
+
sendConnectFour, // 🔴 Connect Four vs AI (minimax depth 4)
|
|
723
|
+
send2048, // 🔢 2048 swipe puzzle
|
|
724
|
+
sendSnake, // 🐍 Snake (swipe + arrow keys)
|
|
725
|
+
sendMemory, // 🧩 Memory Match (8 emoji pairs)
|
|
726
|
+
sendHtmlGame // ⭐ send ANY custom HTML game
|
|
727
|
+
} from 'ishumdz-bail'
|
|
728
|
+
|
|
729
|
+
// One line each:
|
|
730
|
+
await sendChess(sock, m.chat)
|
|
731
|
+
await sendTicTacToe(sock, m.chat)
|
|
732
|
+
await sendConnectFour(sock, m.chat)
|
|
733
|
+
await send2048(sock, m.chat)
|
|
734
|
+
await sendSnake(sock, m.chat)
|
|
735
|
+
await sendMemory(sock, m.chat)
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
Bot command example:
|
|
739
|
+
|
|
740
|
+
```javascript
|
|
741
|
+
const games = {
|
|
742
|
+
chess: sendChess, ttt: sendTicTacToe, c4: sendConnectFour,
|
|
743
|
+
'2048': send2048, snake: sendSnake, memory: sendMemory
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// inside messages.upsert handler:
|
|
747
|
+
const cmd = text.replace('.', '').toLowerCase()
|
|
748
|
+
if (games[cmd]) await games[cmd](sock, msg.key.remoteJid)
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
Custom game (your own HTML):
|
|
752
|
+
|
|
753
|
+
```javascript
|
|
754
|
+
await sendHtmlGame(sock, jid, '<style>...</style><body>...<script>/* your game */</script></body>', '🎯 My Game')
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
> ℹ️ Requires a WhatsApp client that supports GenAI HTML cards (recent Android/iOS). Older clients and WhatsApp Web will not render the card.
|
|
758
|
+
|
|
758
759
|
### 🃏 Blackjack
|
|
759
760
|
|
|
760
761
|
```javascript
|
|
@@ -1033,4 +1034,52 @@ Made with ❤️ by **Lovely**
|
|
|
1033
1034
|
|
|
1034
1035
|
## 📄 License
|
|
1035
1036
|
|
|
1036
|
-
MIT
|
|
1037
|
+
MIT
|
|
1038
|
+
|
|
1039
|
+
---
|
|
1040
|
+
|
|
1041
|
+
## 📱 Full Feature Overview
|
|
1042
|
+
|
|
1043
|
+
```
|
|
1044
|
+
╔═══════════════════════════════════════════════════════════════════════════╗
|
|
1045
|
+
║ 📱 MAIN FEATURES ║
|
|
1046
|
+
╠═══════════════════════════════════════════════════════════════════════════╣
|
|
1047
|
+
║ ║
|
|
1048
|
+
║ ┌─────────────────────────────────────────────────────────────────────┐ ║
|
|
1049
|
+
║ │ 🎮 GAMES │ 📞 VOIP CALLER │ ⚡ PRO METHODS │ ║
|
|
1050
|
+
║ │ ├─ Blackjack │ ├─ Auto Answer │ ├─ sendPoll │ ║
|
|
1051
|
+
║ │ ├─ Slots │ ├─ Audio Playback │ ├─ sendPollVote │ ║
|
|
1052
|
+
║ │ ├─ Dice │ └─ VoipClient │ ├─ editMessage │ ║
|
|
1053
|
+
║ │ ├─ Coin Flip │ │ ├─ deleteMessage │ ║
|
|
1054
|
+
║ │ ├─ Roulette │ 📊 POLLS │ ├─ pinMessage │ ║
|
|
1055
|
+
║ │ ├─ Mines │ ├─ Create Poll │ ├─ starMessage │ ║
|
|
1056
|
+
║ │ ├─ Trivia │ ├─ Vote Poll │ ├─ reactMessage │ ║
|
|
1057
|
+
║ │ ├─ RPS │ └─ Get Results │ ├─ sendPresence │
|
|
1058
|
+
║ │ └─ +6 HTML Games │ │ │ ║
|
|
1059
|
+
║ └─────────────────────────────────────────────────────────────────────┘ ║
|
|
1060
|
+
║ ║
|
|
1061
|
+
║ ┌─────────────────────────────────────────────────────────────────────┐ ║
|
|
1062
|
+
║ │ 📢 NEWSLETTER │ 🟢 STATUS │ 💬 CHAT │ ║
|
|
1063
|
+
║ │ ├─ channelVote │ ├─ sendText │ ├─ editMessage │ ║
|
|
1064
|
+
║ │ ├─ newsletterReact │ ├─ sendMedia │ ├─ deleteMessage │ ║
|
|
1065
|
+
║ │ ├─ getMessages │ ├─ readStatus │ ├─ pinMessage │ ║
|
|
1066
|
+
║ │ ├─ searchChannels │ └─ reactStatus │ ├─ unpinMessage │ ║
|
|
1067
|
+
║ │ └─ listChannels │ │ ├─ starMessage │ ║
|
|
1068
|
+
║ └─────────────────────────────────────────────────────────────────────┘ ║
|
|
1069
|
+
║ ║
|
|
1070
|
+
║ ┌─────────────────────────────────────────────────────────────────────┐ ║
|
|
1071
|
+
║ │ 👥 GROUPS │ 👤 PROFILE │ 📰 MEDIA │ ║
|
|
1072
|
+
║ │ ├─ Get Invite Info │ ├─ checkNumber │ ├─ Newsletter Paths │ ║
|
|
1073
|
+
║ │ ├─ Join via Invite │ ├─ setBio │ ├─ Image Upload │ ║
|
|
1074
|
+
║ │ ├─ Set Announcement │ ├─ updateName │ ├─ Video Upload │ ║
|
|
1075
|
+
║ │ ├─ Set Locked │ ├─ setProfilePic │ ├─ Audio Upload │ ║
|
|
1076
|
+
║ │ ├─ Request List │ ├─ removeProfilePic │ ├─ Document Upload │ ║
|
|
1077
|
+
║ │ ├─ Approve │ └─ rejectCall │ └─ Sticker Upload │ ║
|
|
1078
|
+
║ │ └─ Reject │ │ │ ║
|
|
1079
|
+
║ └─────────────────────────────────────────────────────────────────────┘ ║
|
|
1080
|
+
║ ║
|
|
1081
|
+
║ ✅ No Browser Required ✅ Multi-Device ✅ WebSocket Based ║
|
|
1082
|
+
║ ✅ Active Maintenance ✅ MIT License ✅ Full API Coverage ║
|
|
1083
|
+
║ ║
|
|
1084
|
+
╚═══════════════════════════════════════════════════════════════════════════╝
|
|
1085
|
+
```
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chess Game (Meta AI style live HTML) for ishu-md
|
|
3
|
+
* Ported from izuku-mii/Shou-Project (.catur plugin)
|
|
4
|
+
*
|
|
5
|
+
* Sends an interactive chess-vs-AI board that renders inside
|
|
6
|
+
* WhatsApp's GenAI HTML primitive (sandboxed WebView) on clients
|
|
7
|
+
* that support it. The game runs fully client-side in the chat.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* import { sendChess } from '<pkg>'; // re-exported from Games
|
|
11
|
+
* await sendChess(sock, m.chat); // e.g. on ".chess" command
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export const CHESS_HTML = "<style>*{-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;touch-action:manipulation}</style>\n<body style=\"margin:0;background:transparent;font-family:Arial,sans-serif;color:#eee;touch-action:manipulation;cursor:pointer;overflow:hidden\">\n<div style=\"width:100%;max-width:580px;margin:auto;padding:12px;box-sizing:border-box\">\n<div style=\"background:rgba(255,255,255,.06);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid rgba(255,255,255,.15);border-radius:16px;overflow:hidden;box-shadow:0 8px 32px rgba(0,0,0,.35)\">\n<div style=\"padding:12px 16px;border-bottom:1px solid rgba(255,255,255,.12);display:flex;justify-content:space-between;align-items:center\">\n<div><div style=\"font-size:10px;letter-spacing:1.5px;color:rgba(255,255,255,.45)\">♚ CHESS</div><div style=\"font-size:18px;font-weight:bold;color:#fff\">Official Chess vs AI</div></div>\n<div style=\"text-align:right\"><div id=\"status\" style=\"font-size:13px;font-weight:bold;color:#ffd700;transition:all .3s\">Your Turn</div><div id=\"captured\" style=\"font-size:10px;color:rgba(255,255,255,.4);margin-top:2px\"></div></div>\n</div>\n<div style=\"padding:12px;position:relative\">\n<canvas id=\"board\" width=\"480\" height=\"480\" style=\"width:100%;height:auto;background:rgba(255,255,255,.03);border-radius:12px;display:block;touch-action:none;cursor:pointer\"></canvas>\n<div style=\"display:flex;justify-content:space-between;padding:6px 4px 0;font-size:10px;color:rgba(255,255,255,.3)\">\n<span>♔ You (White)</span>\n<span>♚ AI (Black)</span>\n</div>\n</div></div></div>\n<script>\nconst canvas=document.getElementById('board'),ctx=canvas.getContext('2d');\nconst statusEl=document.getElementById('status'),capturedEl=document.getElementById('captured');\nconst SIZE=480,CELL=SIZE/8;\nlet board=[],selected=null,validMoves=[];\nlet turn='white',gameOver=false,aiThinking=false,moveLock=false;\nlet capturedWhite=[],capturedBlack=[];\nlet enPassantTarget=null;\nlet castlingRights={K:true,Q:true,k:true,q:true};\nlet moveHistory=[];\n\nfunction createBoard(){\nconst b=[];const pieces=['rook','knight','bishop','queen','king','bishop','knight','rook'];\nfor(let r=0;r<8;r++){b[r]=[];for(let c=0;c<8;c++){if(r===0)b[r][c]={type:pieces[c],color:'black'};else if(r===1)b[r][c]={type:'pawn',color:'black'};else if(r===6)b[r][c]={type:'pawn',color:'white'};else if(r===7)b[r][c]={type:pieces[c],color:'white'};else b[r][c]=null;}}\nreturn b;\n}\n\nfunction cloneBoard(b){return b.map(r=>r.map(p=>p?{...p}:null));}\n\nfunction findKing(b,color){for(let r=0;r<8;r++)for(let c=0;c<8;c++){if(b[r][c]&&b[r][c].type==='king'&&b[r][c].color===color)return{r,c};}return null;}\n\nfunction inBounds(r,c){return r>=0&&r<8&&c>=0&&c<8;}\n\nfunction getMoves(b,r,c,checkKing=true,checkCastle=true){\nconst p=b[r][c];if(!p)return[];\nconst moves=[];const enemy=p.color==='white'?'black':'white';\nconst add=(tr,tc)=>{if(inBounds(tr,tc)&&(!b[tr][tc]||b[tr][tc].color!==p.color))moves.push({r:tr,c:tc})};\nconst addSlide=(dr,dc)=>{let tr=r+dr,tc=c+dc;while(inBounds(tr,tc)){if(b[tr][tc]){if(b[tr][tc].color!==p.color)moves.push({r:tr,c:tc});break;}moves.push({r:tr,c:tc});tr+=dr;tc+=dc;}};\nswitch(p.type){\ncase'pawn':{\nconst dir=p.color==='white'?-1:1;const start=p.color==='white'?6:1;\nif(inBounds(r+dir,c)&&!b[r+dir][c]){moves.push({r:r+dir,c});if(r===start&&!b[r+2*dir][c])moves.push({r:r+2*dir,c});}\nfor(const dc of[-1,1]){const nr=r+dir,nc=c+dc;if(inBounds(nr,nc)&&b[nr][nc]&&b[nr][nc].color===enemy)moves.push({r:nr,c:nc});}\nif(enPassantTarget&&enPassantTarget.r===r+dir&&Math.abs(enPassantTarget.c-c)===1){moves.push({r:enPassantTarget.r,c:enPassantTarget.c,enPassant:true});}\nbreak;\n}\ncase'knight':{const offsets=[[-2,-1],[-2,1],[-1,-2],[-1,2],[1,-2],[1,2],[2,-1],[2,1]];for(const[d1,d2]of offsets)add(r+d1,c+d2);break;}\ncase'bishop':{addSlide(1,1);addSlide(1,-1);addSlide(-1,1);addSlide(-1,-1);break;}\ncase'rook':{addSlide(1,0);addSlide(-1,0);addSlide(0,1);addSlide(0,-1);break;}\ncase'queen':{addSlide(1,1);addSlide(1,-1);addSlide(-1,1);addSlide(-1,-1);addSlide(1,0);addSlide(-1,0);addSlide(0,1);addSlide(0,-1);break;}\ncase'king':{\nconst offsets=[[-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1]];\nfor(const[d1,d2]of offsets)add(r+d1,c+d2);\nif(checkCastle&&!isInCheck(b,p.color)){\nconst row=p.color==='white'?7:0;\nif(castlingRights[p.color==='white'?'K':'k']&&!b[row][5]&&!b[row][6]&&b[row][7]&&b[row][7].type==='rook'&&b[row][7].color===p.color&&!isInCheckAfter(b,row,4,row,5,p.color)&&!isInCheckAfter(b,row,4,row,6,p.color)){moves.push({r:row,c:6,castling:'kingside'});}\nif(castlingRights[p.color==='white'?'Q':'q']&&!b[row][3]&&!b[row][2]&&!b[row][1]&&b[row][0]&&b[row][0].type==='rook'&&b[row][0].color===p.color&&!isInCheckAfter(b,row,4,row,3,p.color)&&!isInCheckAfter(b,row,4,row,2,p.color)){moves.push({r:row,c:2,castling:'queenside'});}\n}\nbreak;\n}\n}\nif(checkKing){return moves.filter(m=>!isInCheckAfter(b,r,c,m.r,m.c,p.color));}\nreturn moves;\n}\n\nfunction isInCheckAfter(b,fr,fc,tr,tc,color){\nconst test=cloneBoard(b);\nif(test[tr][tc]&&test[tr][tc].type==='king')return true;\ntest[tr][tc]=test[fr][fc];test[fr][fc]=null;\nif(test[tr][tc]&&test[tr][tc].type==='king'&&Math.abs(fc-tc)>1){const row=fr;test[row][tc>fc?5:3]=test[row][tc>fc?7:0];test[row][tc>fc?7:0]=null;}\nreturn isInCheck(test,color);\n}\n\nfunction isInCheck(b,color){\nconst king=findKing(b,color);if(!king)return false;\nconst enemy=color==='white'?'black':'white';\nfor(let r=0;r<8;r++)for(let c=0;c<8;c++){if(b[r][c]&&b[r][c].color===enemy){const moves=getMoves(b,r,c,false,false);if(moves.some(m=>m.r===king.r&&m.c===king.c))return true;}}\nreturn false;\n}\n\nfunction getAllMoves(b,color){\nconst all=[];\nfor(let r=0;r<8;r++)for(let c=0;c<8;c++){if(b[r][c]&&b[r][c].color===color){const moves=getMoves(b,r,c);for(const m of moves)all.push({fr:r,fc:c,tr:m.r,tc:m.c,castling:m.castling||null,enPassant:m.enPassant||false});}}\nreturn all;\n}\n\nfunction makeMove(b,fr,fc,tr,tc,castling=null,enPassant=false){\nconst nb=cloneBoard(b);\nnb[tr][tc]=nb[fr][fc];nb[fr][fc]=null;\nif(enPassant){nb[fr][tc]=null;}\nif(castling==='kingside'){const row=fr;nb[row][5]=nb[row][7];nb[row][7]=null;}\nif(castling==='queenside'){const row=fr;nb[row][3]=nb[row][0];nb[row][0]=null;}\nreturn nb;\n}\n\nfunction evaluate(b){\nconst values={pawn:1,knight:3,bishop:3.2,rook:5,queueen:9,king:100};\nlet score=0;\nfor(let r=0;r<8;r++)for(let c=0;c<8;c++){if(b[r][c]){const v=values[b[r][c].type]||0;score+=b[r][c].color==='white'?v:-v;}}\nreturn score;\n}\n\nfunction minimax(b,depth,alpha,beta,isMax,aiColor){\nconst moves=getAllMoves(b,isMax?aiColor:(aiColor==='white'?'black':'white'));\nif(depth===0||moves.length===0){return{eval:evaluate(b)};}\nlet best=isMax?{eval:-Infinity}:{eval:Infinity};\nfor(const m of moves){\nconst nb=makeMove(b,m.fr,m.fc,m.tr,m.tc,m.castling,m.enPassant);\nconst result=minimax(nb,depth-1,alpha,beta,!isMax,aiColor);\nif(isMax){if(result.eval>best.eval){best={...result,fr:m.fr,fc:m.fc,tr:m.tr,tc:m.tc,castling:m.castling,enPassant:m.enPassant}}alpha=Math.max(alpha,result.eval)}else{if(result.eval<best.eval){best={...result,fr:m.fr,fc:m.fc,tr:m.tr,tc:m.tc,castling:m.castling,enPassant:m.enPassant}}beta=Math.min(beta,result.eval)}\nif(beta<=alpha)break;\n}\nreturn best;\n}\n\nfunction aiMove(){\nif(aiThinking||gameOver||moveLock)return;\naiThinking=true;moveLock=true;statusEl.textContent='AI thinking...';\nsetTimeout(()=>{\nconst aiColor='black';const moves=getAllMoves(board,aiColor);\nif(moves.length===0){gameOver=true;statusEl.textContent='You Win! Checkmate!';aiThinking=false;moveLock=false;return;}\nconst result=minimax(board,3,-Infinity,Infinity,true,aiColor);\nif(result.fr!==undefined){\nconst target=board[result.tr][result.tc];\nif(target){if(target.color==='white')capturedWhite.push(target.type);}\nboard=makeMove(board,result.fr,result.fc,result.tr,result.tc,result.castling,result.enPassant);\nif(result.castling)updateCastlingRights(result.fr,true,true);\nenPassantTarget=null;\nconst piece=board[result.tr][result.tc];if(piece&&piece.type==='pawn'&&Math.abs(result.fr-result.tr)===2){enPassantTarget={r:(result.fr+result.tr)/2,c:result.fc};}\nupdateCaptured();\nconst check=isInCheck(board,'white');\nconst allMoves=getAllMoves(board,'white');\nif(allMoves.length===0){gameOver=true;statusEl.textContent=check?'Checkmate! AI Wins':'Stalemate! Draw';}else{turn='white';statusEl.textContent=check?'♚ CHECK! Your Turn':'Your Turn';}\n}\naiThinking=false;moveLock=false;selected=null;validMoves=[];\n},300);\n}\n\nfunction updateCastlingRights(fr,fc,color){}\n\nfunction updateCaptured(){\nlet txt='';\nif(capturedBlack.length){txt+='White: '+capturedBlack.map(t=>{const s={pawn:'♙',knight:'♘',bishop:'♗',rook:'♖',queen:'♕',king:'♔'};return s[t]||t}).join(' ')}\nif(capturedBlack.length&&capturedWhite.length)txt+=' | ';\nif(capturedWhite.length){txt+='Black: '+capturedWhite.map(t=>{const s={pawn:'♟',knight:'♞',bishop:'♝',rook:'♜',queen:'♛',king:'♚'};return s[t]||t}).join(' ')}\ncapturedEl.textContent=txt;\n}\n\nfunction drawPiece(r,c,p){\nconst x=c*CELL+CELL/2,y=r*CELL+CELL/2;\nconst s=CELL/42;\nctx.save();ctx.translate(x,y);\nconst isWhite=p.color==='white';\nctx.shadowColor=isWhite?'rgba(0,0,0,.12)':'rgba(0,0,0,.5)';\nctx.shadowBlur=8*s;\nconst baseColor=isWhite?'#f8f8f8':'#1a1a1a';\nconst shade=isWhite?'#e0e0e0':'#2a2a2a';\nconst dark=isWhite?'#666':'#aaa';\nconst accent=isWhite?'#444':'#eee';\nctx.fillStyle=baseColor;ctx.strokeStyle=dark;ctx.lineWidth=1.5*s;\nswitch(p.type){\ncase'king':{\nctx.beginPath();ctx.arc(0,0,18*s,0,Math.PI*2);ctx.fill();ctx.stroke();\nctx.fillStyle=shade;ctx.fillRect(-12*s,-14*s,24*s,4*s);\nctx.fillRect(-4*s,-20*s,8*s,8*s);\nctx.fillStyle=accent;ctx.fillRect(-2*s,-10*s,4*s,10*s);\nctx.fillRect(-8*s,-4*s,16*s,2*s);\nctx.fillStyle=dark;ctx.fillRect(-1*s,-6*s,2*s,4*s);\nif(!isWhite){ctx.fillStyle='#fff';ctx.fillRect(-1*s,-6*s,2*s,4*s);}\nbreak;\n}\ncase'queen':{\nctx.beginPath();ctx.arc(0,0,17*s,0,Math.PI*2);ctx.fill();ctx.stroke();\nctx.fillStyle=shade;\nfor(let i=-2;i<=2;i++){ctx.fillRect(i*8*s-2*s,-18*s,4*s,12*s)}\nctx.fillRect(-14*s,-6*s,28*s,4*s);\nctx.fillStyle=accent;\nfor(let i=-2;i<=2;i++){ctx.fillRect(i*8*s-1*s,-14*s,2*s,5*s)}\nctx.fillStyle=dark;for(let i=-2;i<=2;i++){if(i!==0){ctx.fillRect(i*8*s-1*s,-10*s,2*s,3*s)}}\nbreak;\n}\ncase'bishop':{\nctx.beginPath();ctx.arc(0,0,16*s,0,Math.PI*2);ctx.fill();ctx.stroke();\nctx.fillStyle=shade;\nctx.beginPath();ctx.moveTo(0,-21*s);ctx.lineTo(-10*s,-2*s);ctx.lineTo(10*s,-2*s);ctx.closePath();ctx.fill();\nctx.fillRect(-2*s,-2*s,4*s,7*s);\nctx.fillStyle=accent;\nctx.beginPath();ctx.moveTo(0,-17*s);ctx.lineTo(-6*s,-5*s);ctx.lineTo(6*s,-5*s);ctx.closePath();ctx.fill();\nctx.fillStyle=dark;ctx.fillRect(-1*s,-10*s,2*s,4*s);\nbreak;\n}\ncase'knight':{\nctx.beginPath();ctx.arc(0,0,16*s,0,Math.PI*2);ctx.fill();ctx.stroke();\nctx.fillStyle=shade;\nctx.beginPath();ctx.moveTo(-10*s,-4*s);ctx.quadraticCurveTo(-4*s,-21*s,8*s,-9*s);ctx.quadraticCurveTo(0,-6*s,10*s,-2*s);ctx.closePath();ctx.fill();\nctx.fillStyle=accent;\nctx.beginPath();ctx.moveTo(-6*s,-6*s);ctx.quadraticCurveTo(-2*s,-16*s,5*s,-9*s);ctx.quadraticCurveTo(0,-7*s,6*s,-4*s);ctx.closePath();ctx.fill();\nctx.fillStyle=dark;ctx.fillRect(-2*s,-8*s,2*s,3*s);\nbreak;\n}\ncase'rook':{\nctx.beginPath();ctx.arc(0,0,16*s,0,Math.PI*2);ctx.fill();ctx.stroke();\nctx.fillStyle=shade;\nctx.fillRect(-12*s,-16*s,24*s,6*s);\nctx.fillRect(-10*s,-22*s,8*s,8*s);\nctx.fillRect(2*s,-22*s,8*s,8*s);\nctx.fillStyle=accent;\nctx.fillRect(-12*s,-14*s,24*s,2*s);\nctx.fillRect(-8*s,-20*s,6*s,6*s);\nctx.fillRect(2*s,-20*s,6*s,6*s);\nctx.fillStyle=dark;ctx.fillRect(-1*s,-12*s,2*s,4*s);\nbreak;\n}\ncase'pawn':{\nctx.beginPath();ctx.arc(0,0,14*s,0,Math.PI*2);ctx.fill();ctx.stroke();\nctx.fillStyle=shade;\nctx.beginPath();ctx.arc(0,-5*s,9*s,0,Math.PI*2);ctx.fill();ctx.stroke();\nctx.fillRect(-2*s,0,4*s,8*s);\nctx.fillStyle=accent;\nctx.beginPath();ctx.arc(0,-5*s,6*s,0,Math.PI*2);ctx.fill();\nctx.fillStyle=dark;ctx.fillRect(-1*s,0,2*s,4*s);\nbreak;\n}\n}\nctx.restore();\n}\n\nfunction drawBoard(){\nctx.clearRect(0,0,SIZE,SIZE);\nfor(let r=0;r<8;r++)for(let c=0;c<8;c++){\nconst light=(r+c)%2===0;ctx.fillStyle=light?'#f0d9b5':'#b58863';ctx.fillRect(c*CELL,r*CELL,CELL,CELL);\n}\nif(selected&&!moveLock){ctx.fillStyle='rgba(255,255,0,.3)';ctx.fillRect(selected.c*CELL,selected.r*CELL,CELL,CELL);}\nif(!moveLock){for(const m of validMoves){if(board[m.r][m.c]){ctx.fillStyle='rgba(255,50,50,.4)';ctx.fillRect(m.c*CELL,m.r*CELL,CELL,CELL);}else{ctx.fillStyle='rgba(0,0,0,.2)';ctx.beginPath();ctx.arc(m.c*CELL+CELL/2,m.r*CELL+CELL/2,9,0,Math.PI*2);ctx.fill();}}}\nconst kw=findKing(board,'white');const kb=findKing(board,'black');\nif(kw&&isInCheck(board,'white')){ctx.fillStyle='rgba(255,0,0,.35)';ctx.fillRect(kw.c*CELL,kw.r*CELL,CELL,CELL);ctx.strokeStyle='rgba(255,0,0,.9)';ctx.lineWidth=3;ctx.strokeRect(kw.c*CELL+4,kw.r*CELL+4,CELL-8,CELL-8);ctx.strokeStyle='rgba(255,50,50,.5)';ctx.lineWidth=1;ctx.setLineDash([4,6]);ctx.strokeRect(kw.c*CELL+8,kw.r*CELL+8,CELL-16,CELL-16);ctx.setLineDash([]);}\nif(kb&&isInCheck(board,'black')){ctx.fillStyle='rgba(255,0,0,.35)';ctx.fillRect(kb.c*CELL,kb.r*CELL,CELL,CELL);ctx.strokeStyle='rgba(255,0,0,.9)';ctx.lineWidth=3;ctx.strokeRect(kb.c*CELL+4,kb.r*CELL+4,CELL-8,CELL-8);}\nfor(let r=0;r<8;r++)for(let c=0;c<8;c++){if(board[r][c])drawPiece(r,c,board[r][c]);}\nctx.fillStyle='rgba(255,255,255,.06)';ctx.font='10px Arial';\nfor(let i=0;i<8;i++){ctx.fillText('abcdefgh'[i],i*CELL+5,SIZE-3);ctx.fillText(8-i,4,i*CELL+14)}\n}\n\nfunction handleClick(r,c){\nif(gameOver||turn!=='white'||aiThinking||moveLock)return;\nif(selected){const valid=validMoves.some(m=>m.r===r&&m.c===c);if(valid){const move=validMoves.find(m=>m.r===r&&m.c===c);const target=board[r][c];if(target&&target.color==='black')capturedWhite.push(target.type);\nlet castling=move.castling||null;let ep=move.enPassant||false;\nboard=makeMove(board,selected.r,selected.c,r,c,castling,ep);\nif(castling){const row=selected.r;board[row][castling==='kingside'?5:3]=board[row][castling==='kingside'?7:0];board[row][castling==='kingside'?7:0]=null;}\nenPassantTarget=null;const piece=board[r][c];if(piece&&piece.type==='pawn'&&Math.abs(selected.r-r)===2){enPassantTarget={r:(selected.r+r)/2,c:selected.c};}\nselected=null;validMoves=[];turn='black';updateCaptured();\nconst check=isInCheck(board,'black');const allMoves=getAllMoves(board,'black');\nif(allMoves.length===0){gameOver=true;statusEl.textContent=check?'You Win! Checkmate':'Stalemate! Draw';return;}\nstatusEl.textContent='AI turn...';setTimeout(()=>aiMove(),350);return;}\nselected=null;validMoves=[];}\nif(board[r][c]&&board[r][c].color==='white'&&!moveLock){const moves=getMoves(board,r,c);if(moves.length>0){selected={r,c};validMoves=moves;}}\n}\n\ncanvas.addEventListener('click',function(e){const rect=canvas.getBoundingClientRect();const scale=SIZE/rect.width;const x=(e.clientX-rect.left)*scale;const y=(e.clientY-rect.top)*scale;const c=Math.floor(x/CELL),r=Math.floor(y/CELL);if(r>=0&&r<8&&c>=0&&c<8)handleClick(r,c);});\ncanvas.addEventListener('touchstart',function(e){e.preventDefault();const t=e.touches[0];if(!t)return;const rect=canvas.getBoundingClientRect();const scale=SIZE/rect.width;const x=(t.clientX-rect.left)*scale;const y=(t.clientY-rect.top)*scale;const c=Math.floor(x/CELL),r=Math.floor(y/CELL);if(r>=0&&r<8&&c>=0&&c<8)handleClick(r,c);});\n\nfunction resetGame(){board=createBoard();selected=null;validMoves=[];turn='white';gameOver=false;aiThinking=false;moveLock=false;capturedWhite=[];capturedBlack=[];enPassantTarget=null;castlingRights={K:true,Q:true,k:true,q:true};statusEl.textContent='Your Turn';updateCaptured();drawBoard();}\nresetGame();\nsetInterval(drawBoard,50);\n</script></body>";
|
|
15
|
+
|
|
16
|
+
const BOT_RESPONSE_ID = "b2e40280-433c-45d8-9c1a-270bec558860";
|
|
17
|
+
const RESPONSE_ID = "4db57b2c-8393-484d-8b9a-8e6d1a14b349";
|
|
18
|
+
const VERIFICATION_SIGNATURE = "TklYRUwuTWVzc2FnZUJ1aWxkZXJWNC43LVZlcmlmaWNhdGlvblNpZ25hdHVyZS5NZXRhZGF0YeN55YRyad2+ZA==";
|
|
19
|
+
const CERTIFICATE_CHAIN = [
|
|
20
|
+
"TklYRUwuTWVzc2FnZUJ1aWxkZXJWNC43LUNlcnRpZmljYXRlQ2hhaW4uTWV0YWRhdGEOvtJr968bbpKdZreOTwkk9aPN++XPE60RfuzNLkXXc7LE8BOkJOWRpo2oNXaRJ3uCNJ43HY3A+oetnvHSfcxWqmvvTSrBOI5V1NOD6RMsZ/st1XVPUx83AGps1l5jYBOYzqMNy6un2tToJ2Bt9bXRo29tWLZTu8m7TNY/hISwVpVc5tjSet5U7btPN+dMIx2UvykB1jcbWGsdklheeuz8RXSStNXzeaGvsf1lpZ/ugLE4b2BdmlRNKrY6zLE4qFtRYQoS7axOyQX+4QUyN2m9bfm7urQmn+QRSXJwMO7X5kAJJLbkVGJFt9Pm9VXPwQVrK2aaqiXlpusj+7DfDw00OULmYMmZDTqXM0nUVLxj13z0LhMQoQhhNG8utdUn4uKOFceliTZ/xiP+A54GnX9620641bqw3ctfh9NNXPsTEK8hAUD7FDqUhVntHmoEYYEHq8X1tHHZYP49/f2iezTiE8AUaoZo42/jIWQIKohOGNUib2hEqMkW8NsR8vPihvNuqPc0zKZcl6359YFQdjiiW8kCRD/rsDOr9v1eYLFZKYloFyzFqEgj+jcG/V47elOjShJ5CCPwatXwP6HIloVwtgygFsnOFmCg6Ojoivfoz8Nw1qxFwg5OU2cq/1WbWNELKnaFg4eUWCAIJ/3ZIJsEPkgemZxGhE+hdiNn9dkQYBJs1kx2BxdIkJmQ9vJSKkrMz6lTxZM3IJ9mhmKS6zYdU1ppeAao0/ayte997DQParb/AHLN79g0iW1ad0z8ir5jAl0q3a+UZPTSa4YiSqC2PZ/gfxG5wvL2mKmeKowG0RXjmEp5iNxrni+T/HRLZOoH7y0DQ24nMCPg",
|
|
21
|
+
"TklYRUwuTWVzc2FnZUJ1aWxkZXJWNC43LUNlcnRpZmljYXRlQ2hhaW4uTWV0YWRhdGHsL0Ccm0ELINFZ2IaBhKaeWnVuh0o6nZLCioCn9xpSADzwIS5VCWO+1eVXT2atJOyf7FYlpB0/JA3Us+aQtekuIkHu/zBXijORZ4ClF4+sF3cSTNg6gY/+6iwLK/zs3bMg+GeJrcI65vXfs95Shxlb2Rd5GRT2/2yBmR6Zkf5QwMJuptUHWtM26WY7/xlkEKGFYDZVqOSylusiOzSALa815zC6dCiHoJNLBEKMlaZZQOk57/+OYoU5zzTaEgLhyvNFHSyAlyLQ3SGFtVHAaJZHSmmSPyJowCOB+92Gkk6SWVMsk6FbU8QJWFtlhzV/W/gZ7WzUlS/AKgN0th9/cq20ToFkW7X9c+rtYavufmuieqFhXgaMD8AGsoN9QC/HzNC9D1nydPfFYEUr9BHVy2nF5gM58Y59r2rT8p5LPARIkUp8g+5DLhyW0tdZFZ1305o4AHCayZnp5rjcU2Xi/c1Qf/djBGakmijlMs4aMzKJYD0c4Q8jdI7sNyd876K2wRD+L6KeD2QB3PtCS4P7BWAl5gh5CJ6ZBrwcaKXZqcSjEwm52MqVCgYZdapAaNYUy/QndttjLOG0wxxwuX1hIhMjPnIKZR1kwnqD5EqlHpilrnojRZvjVGN4zEKmilS8rNstt4HHs/D849W+Q6LRVWiWMs0cT2IugrX+Skxd8En7Gq52UEmuVBrSTpN+UpIu20NsVb9lsvuYh3XO441606tOEY2eKcZJdTtqrOTNqbbTk0zVn1yhbOCvmfctBNDhTwaC5QMi0P9wjU5XI9SBtkdQLizc5oqpoiHeqgb8+aJHVLcbgIJ/KLZKtRWFDfzRNM02Csx4etUUapVd2NA/L0oMs/O5T9sVj9FBJ7q99GWr3PVmxJb36mHZLXC4k1gGN9swE0LtzYsUdT5tUo9ri/hS3W/SM+F1p4Kh4QIgRcG3ciIHGN44bnDh3HDCz0fDnzKYw0bclMxZPctEyJ5gEOPF6OAkjD9dEaRGq/tEPf1k9Aub+v2dEjnfrYWAm4E5Zfhs2Xh0CT0k+SzhgKd0K/46ChJ20G5+blwpIvahvTVS68+aVIX6CwXs4tcVx6FnmVsMOOkIasfaqQLZYbNBkuLoZnQAq4j8yRekrQ=="
|
|
22
|
+
];
|
|
23
|
+
const BOT_JID = "867051314767696@bot";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Build the raw relayMessage content for the chess game.
|
|
27
|
+
* @returns {object} message content for sock.relayMessage()
|
|
28
|
+
*/
|
|
29
|
+
export function buildChessMessage() {
|
|
30
|
+
return {
|
|
31
|
+
messageContextInfo: {
|
|
32
|
+
deviceListMetadata: {},
|
|
33
|
+
deviceListMetadataVersion: 2,
|
|
34
|
+
botMetadata: {
|
|
35
|
+
messageDisclaimerText: "",
|
|
36
|
+
botResponseId: BOT_RESPONSE_ID,
|
|
37
|
+
verificationMetadata: {
|
|
38
|
+
proofs: [{
|
|
39
|
+
version: 1,
|
|
40
|
+
useCase: 1,
|
|
41
|
+
signature: VERIFICATION_SIGNATURE,
|
|
42
|
+
certificateChain: CERTIFICATE_CHAIN
|
|
43
|
+
}]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
botForwardedMessage: {
|
|
48
|
+
message: {
|
|
49
|
+
richResponseMessage: {
|
|
50
|
+
messageType: 1,
|
|
51
|
+
submessages: [{
|
|
52
|
+
messageType: 2,
|
|
53
|
+
messageText: "\u265A Chess vs AI"
|
|
54
|
+
}],
|
|
55
|
+
unifiedResponse: {
|
|
56
|
+
data: Buffer.from(JSON.stringify({
|
|
57
|
+
response_id: RESPONSE_ID,
|
|
58
|
+
sections: [{
|
|
59
|
+
view_model: {
|
|
60
|
+
primitive: {
|
|
61
|
+
__typename: "GenAIaeacdsnwHtmlPrimitive",
|
|
62
|
+
payload: CHESS_HTML,
|
|
63
|
+
trusted_sources: ["nixel.dev"]
|
|
64
|
+
},
|
|
65
|
+
__typename: "GenAISingleLayoutViewModel"
|
|
66
|
+
}
|
|
67
|
+
}]
|
|
68
|
+
})).toString("base64")
|
|
69
|
+
},
|
|
70
|
+
contextInfo: {
|
|
71
|
+
forwardingScore: 1,
|
|
72
|
+
isForwarded: true,
|
|
73
|
+
forwardedAiBotMessageInfo: {
|
|
74
|
+
botJid: BOT_JID
|
|
75
|
+
},
|
|
76
|
+
forwardOrigin: 4
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Send the interactive chess game (Meta AI style HTML card) to a chat.
|
|
86
|
+
* @param {object} sock WASocket instance (from makeWASocket)
|
|
87
|
+
* @param {string} jid target chat JID (user or group)
|
|
88
|
+
* @returns {Promise<any>} relayMessage result
|
|
89
|
+
*/
|
|
90
|
+
export async function sendChess(sock, jid) {
|
|
91
|
+
return sock.relayMessage(jid, buildChessMessage(), {});
|
|
92
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connect Four - live HTML game for ishu-md
|
|
3
|
+
* 7x6 board, tap a column to drop a disc, AI opponent.
|
|
4
|
+
* Usage: await sendConnectFour(sock, jid)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { buildHtmlGameMessage } from './htmlGame.js';
|
|
8
|
+
|
|
9
|
+
const CONNECT4_HTML = "<style>*{-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;touch-action:manipulation}</style>" +
|
|
10
|
+
"<body style=\"margin:0;background:transparent;font-family:Arial,sans-serif;color:#eee;touch-action:manipulation;overflow:hidden\">" +
|
|
11
|
+
"<div style=\"width:100%;max-width:420px;margin:auto;padding:12px;box-sizing:border-box\">" +
|
|
12
|
+
"<div style=\"background:rgba(255,255,255,.06);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid rgba(255,255,255,.15);border-radius:16px;overflow:hidden;box-shadow:0 8px 32px rgba(0,0,0,.35)\">" +
|
|
13
|
+
"<div style=\"padding:12px 16px;border-bottom:1px solid rgba(255,255,255,.12);display:flex;justify-content:space-between;align-items:center\">" +
|
|
14
|
+
"<div><div style=\"font-size:10px;letter-spacing:1.5px;color:rgba(255,255,255,.45)\">\uD83D\uDD34 CONNECT FOUR</div><div style=\"font-size:18px;font-weight:bold;color:#fff\">You vs AI</div></div>" +
|
|
15
|
+
"<div id=\"status\" style=\"font-size:13px;font-weight:bold;color:#ffd700;text-align:right\">Your Turn</div>" +
|
|
16
|
+
"</div>" +
|
|
17
|
+
"<div style=\"padding:14px\">" +
|
|
18
|
+
"<canvas id=\"board\" width=\"350\" height=\"300\" style=\"width:100%;height:auto;background:#1a4fa0;border-radius:12px;display:block;touch-action:none;cursor:pointer\"></canvas>" +
|
|
19
|
+
"<div style=\"display:flex;justify-content:space-between;max-width:350px;margin:12px auto 0;font-size:12px;color:rgba(255,255,255,.6)\">" +
|
|
20
|
+
"<span>You: <b id=\"sw\" style=\"color:#ffd700\">0</b></span><span>Draws: <b id=\"sd\" style=\"color:#fff\">0</b></span><span>AI: <b id=\"sl\" style=\"color:#ff5252\">0</b></span></div>" +
|
|
21
|
+
"<button id=\"new\" style=\"display:block;width:100%;max-width:350px;margin:14px auto 0;padding:12px;border:0;border-radius:12px;background:linear-gradient(135deg,#ffd700,#ffb300);color:#222;font-weight:bold;font-size:15px;cursor:pointer\">New Game</button>" +
|
|
22
|
+
"</div></div></div>" +
|
|
23
|
+
"<script>" +
|
|
24
|
+
"var cv=document.getElementById('board'),ctx=cv.getContext('2d');" +
|
|
25
|
+
"var st=document.getElementById('status');" +
|
|
26
|
+
"var COLS=7,ROWS=6,CELL=50;" +
|
|
27
|
+
"var board,over,lock,score={w:0,d:0,l:0};" +
|
|
28
|
+
"function reset(){board=[];for(var r=0;r<ROWS;r++){board.push([]);for(var c=0;c<COLS;c++)board[r].push(0);}over=false;lock=false;st.textContent='Your Turn';draw();}" +
|
|
29
|
+
"function lowestEmpty(c){for(var r=ROWS-1;r>=0;r--)if(board[r][c]===0)return r;return -1;}" +
|
|
30
|
+
"function checkWin(b,p){for(var r=0;r<ROWS;r++)for(var c=0;c<COLS;c++){if(!b[r][c]||b[r][c]!==p)continue;" +
|
|
31
|
+
"var dirs=[[0,1],[1,0],[1,1],[1,-1]];" +
|
|
32
|
+
"for(var d=0;d<4;d++){var cnt=1;" +
|
|
33
|
+
"for(var s=1;s<4;s++){var nr=r+dirs[d][0]*s,nc=c+dirs[d][1]*s;if(nr>=0&&nr<ROWS&&nc>=0&&nc<COLS&&b[nr][nc]===p)cnt++;else break;}" +
|
|
34
|
+
"for(var s2=1;s2<4;s2++){var nr2=r-dirs[d][0]*s2,nc2=c-dirs[d][1]*s2;if(nr2>=0&&nr2<ROWS&&nc2>=0&&nc2<COLS&&b[nr2][nc2]===p)cnt++;else break;}" +
|
|
35
|
+
"if(cnt>=4)return true;}}return false;}" +
|
|
36
|
+
"function boardFull(b){for(var c=0;c<COLS;c++)if(b[0][c]===0)return false;return true;}" +
|
|
37
|
+
"function evalWindow(w){var you=0,ai=0;for(var i=0;i<4;i++){if(w[i]===2)ai++;else if(w[i]===1)you++;}" +
|
|
38
|
+
"if(you>0&&ai>0)return 0;if(ai===3)return 50;if(ai===2)return 10;if(you===3)return -80;if(you===2)return -8;return 0;}" +
|
|
39
|
+
"function evaluate(b){var s=0;" +
|
|
40
|
+
"for(var r=0;r<ROWS;r++)for(var c=0;c<COLS;c++){" +
|
|
41
|
+
"if(c+3<COLS)s+=evalWindow([b[r][c],b[r][c+1],b[r][c+2],b[r][c+3]]);" +
|
|
42
|
+
"if(r+3<ROWS)s+=evalWindow([b[r][c],b[r+1][c],b[r+2][c],b[r+3][c]]);" +
|
|
43
|
+
"if(r+3<ROWS&&c+3<COLS)s+=evalWindow([b[r][c],b[r+1][c+1],b[r+2][c+2],b[r+3][c+3]]);" +
|
|
44
|
+
"if(r+3<ROWS&&c-3>=0)s+=evalWindow([b[r][c],b[r+1][c-1],b[r+2][c-2],b[r+3][c-3]]);}" +
|
|
45
|
+
"for(var c2=0;c2<COLS;c2++){if(b[3][c2]===2)s+=30;if(b[3][c2]===1)s-=30;}" +
|
|
46
|
+
"return s;}" +
|
|
47
|
+
"function cloneB(b){return b.map(function(r){return r.slice();});}" +
|
|
48
|
+
"function drop(b,c,p){for(var r=ROWS-1;r>=0;r--)if(b[r][c]===0){b[r][c]=p;return true;}return false;}" +
|
|
49
|
+
"function minimax(b,depth,alpha,beta,maximizing){" +
|
|
50
|
+
"if(checkWin(b,2))return{v:100000-depth};if(checkWin(b,1))return{v:-100000+depth};" +
|
|
51
|
+
"if(boardFull(b))return{v:0};if(depth===0)return{v:evaluate(b)};" +
|
|
52
|
+
"var best=maximizing?{v:-Infinity}:{v:Infinity};" +
|
|
53
|
+
"var order=[3,2,4,1,5,0,6];" +
|
|
54
|
+
"for(var i=0;i<7;i++){var c=order[i];if(b[0][c]!==0)continue;" +
|
|
55
|
+
"var nb=cloneB(b);drop(nb,c,maximizing?2:1);" +
|
|
56
|
+
"var res=minimax(nb,depth-1,alpha,beta,!maximizing);" +
|
|
57
|
+
"if(maximizing){if(res.v>best.v)best=res;if(res.v>alpha)alpha=res.v;}" +
|
|
58
|
+
"else{if(res.v<best.v)best=res;if(res.v<beta)beta=res.v;}" +
|
|
59
|
+
"if(beta<=alpha)break;}" +
|
|
60
|
+
"return best;}" +
|
|
61
|
+
"function aiTurn(){lock=true;st.textContent='AI thinking...';" +
|
|
62
|
+
"setTimeout(function(){" +
|
|
63
|
+
"var order=[3,2,4,1,5,0,6],bestC=-1,bestV=-Infinity;" +
|
|
64
|
+
"for(var i=0;i<7;i++){var c=order[i];if(board[0][c]!==0)continue;" +
|
|
65
|
+
"var nb=cloneB(board);drop(nb,c,2);" +
|
|
66
|
+
"var v=minimax(nb,4,-Infinity,Infinity,false).v;" +
|
|
67
|
+
"if(v>bestV){bestV=v;bestC=c;}}" +
|
|
68
|
+
"if(bestC>=0){drop(board,bestC,2);}" +
|
|
69
|
+
"lock=false;draw();" +
|
|
70
|
+
"if(checkWin(board,2)){over=true;st.textContent='AI Wins! \uD83E\uDD16';score.l++;scores();return;}" +
|
|
71
|
+
"if(boardFull(board)){over=true;st.textContent=\"It's a Draw! \uD83E\uDD1D\";score.d++;scores();return;}" +
|
|
72
|
+
"st.textContent='Your Turn';},300);}" +
|
|
73
|
+
"function scores(){document.getElementById('sw').textContent=score.w;document.getElementById('sd').textContent=score.d;document.getElementById('sl').textContent=score.l;}" +
|
|
74
|
+
"function draw(){ctx.clearRect(0,0,350,300);" +
|
|
75
|
+
"for(var r=0;r<ROWS;r++)for(var c=0;c<COLS;c++){" +
|
|
76
|
+
"ctx.beginPath();ctx.arc(c*CELL+CELL/2,r*CELL+CELL/2,CELL/2-4,0,Math.PI*2);" +
|
|
77
|
+
"ctx.fillStyle=board[r][c]===0?'#0d2f66':board[r][c]===1?'#ffd700':'#ff5252';ctx.fill();" +
|
|
78
|
+
"if(board[r][c]!==0){ctx.strokeStyle='rgba(0,0,0,.3)';ctx.lineWidth=2;ctx.stroke();}}}" +
|
|
79
|
+
"cv.addEventListener('click',function(e){" +
|
|
80
|
+
"if(over||lock)return;" +
|
|
81
|
+
"var rect=cv.getBoundingClientRect(),scale=350/rect.width;" +
|
|
82
|
+
"var x=(e.clientX-rect.left)*scale,c=Math.floor(x/CELL);" +
|
|
83
|
+
"if(c<0||c>=COLS||board[0][c]!==0)return;" +
|
|
84
|
+
"drop(board,c,1);draw();" +
|
|
85
|
+
"if(checkWin(board,1)){over=true;st.textContent='You Win! \uD83C\uDF89';score.w++;scores();return;}" +
|
|
86
|
+
"if(boardFull(board)){over=true;st.textContent=\"It's a Draw! \uD83E\uDD1D\";score.d++;scores();return;}" +
|
|
87
|
+
"aiTurn();});" +
|
|
88
|
+
"cv.addEventListener('touchstart',function(e){e.preventDefault();var t=e.touches[0];if(!t)return;" +
|
|
89
|
+
"var rect=cv.getBoundingClientRect(),scale=350/rect.width;" +
|
|
90
|
+
"var x=(t.clientX-rect.left)*scale,c=Math.floor(x/CELL);" +
|
|
91
|
+
"if(over||lock||c<0||c>=COLS||board[0][c]!==0)return;" +
|
|
92
|
+
"drop(board,c,1);draw();" +
|
|
93
|
+
"if(checkWin(board,1)){over=true;st.textContent='You Win! \uD83C\uDF89';score.w++;scores();return;}" +
|
|
94
|
+
"if(boardFull(board)){over=true;st.textContent=\"It's a Draw! \uD83E\uDD1D\";score.d++;scores();return;}" +
|
|
95
|
+
"aiTurn();});" +
|
|
96
|
+
"document.getElementById('new').addEventListener('click',reset);" +
|
|
97
|
+
"reset();scores();" +
|
|
98
|
+
"</script></body>";
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Build the raw relayMessage content for Connect Four.
|
|
102
|
+
*/
|
|
103
|
+
export function buildConnectFourMessage() {
|
|
104
|
+
return buildHtmlGameMessage(CONNECT4_HTML, "\uD83D\uDD34 Connect Four vs AI");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Send the Connect Four game to a chat.
|
|
109
|
+
* @param {object} sock WASocket instance
|
|
110
|
+
* @param {string} jid target chat JID
|
|
111
|
+
*/
|
|
112
|
+
export async function sendConnectFour(sock, jid) {
|
|
113
|
+
return sock.relayMessage(jid, buildConnectFourMessage(), {});
|
|
114
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 2048 - live HTML game for ishu-md
|
|
3
|
+
* Swipe to merge tiles, reach 2048!
|
|
4
|
+
* Usage: await send2048(sock, jid)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { buildHtmlGameMessage } from './htmlGame.js';
|
|
8
|
+
|
|
9
|
+
const GAME_2048_HTML = "<style>*{-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;touch-action:none}</style>" +
|
|
10
|
+
"<body style=\"margin:0;background:transparent;font-family:Arial,sans-serif;color:#eee;touch-action:none;overflow:hidden\">" +
|
|
11
|
+
"<div style=\"width:100%;max-width:420px;margin:auto;padding:12px;box-sizing:border-box\">" +
|
|
12
|
+
"<div style=\"background:rgba(255,255,255,.06);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid rgba(255,255,255,.15);border-radius:16px;overflow:hidden;box-shadow:0 8px 32px rgba(0,0,0,.35)\">" +
|
|
13
|
+
"<div style=\"padding:12px 16px;border-bottom:1px solid rgba(255,255,255,.12);display:flex;justify-content:space-between;align-items:center\">" +
|
|
14
|
+
"<div><div style=\"font-size:10px;letter-spacing:1.5px;color:rgba(255,255,255,.45)\">\uD83D\uDD24 2048</div><div style=\"font-size:18px;font-weight:bold;color:#fff\">Merge to 2048!</div></div>" +
|
|
15
|
+
"<div style=\"text-align:right\"><div style=\"font-size:10px;color:rgba(255,255,255,.5)\">SCORE</div><div id=\"score\" style=\"font-size:18px;font-weight:bold;color:#ffd700\">0</div></div>" +
|
|
16
|
+
"</div>" +
|
|
17
|
+
"<div style=\"padding:14px\">" +
|
|
18
|
+
"<canvas id=\"board\" width=\"360\" height=\"360\" style=\"width:100%;height:auto;background:rgba(255,255,255,.04);border-radius:12px;display:block;touch-action:none\"></canvas>" +
|
|
19
|
+
"<div style=\"display:flex;justify-content:space-between;align-items:center;max-width:360px;margin:12px auto 0\">" +
|
|
20
|
+
"<span id=\"best\" style=\"font-size:12px;color:rgba(255,255,255,.6)\">Best: 0</span>" +
|
|
21
|
+
"<button id=\"new\" style=\"padding:10px 24px;border:0;border-radius:12px;background:linear-gradient(135deg,#ffd700,#ffb300);color:#222;font-weight:bold;font-size:14px;cursor:pointer\">New Game</button>" +
|
|
22
|
+
"</div>" +
|
|
23
|
+
"<div style=\"text-align:center;font-size:11px;color:rgba(255,255,255,.4);margin-top:8px\">Swipe to move tiles</div>" +
|
|
24
|
+
"</div></div></div>" +
|
|
25
|
+
"<script>" +
|
|
26
|
+
"var cv=document.getElementById('board'),ctx=cv.getContext('2d');" +
|
|
27
|
+
"var S=360,C=4,CELL=90;" +
|
|
28
|
+
"var grid,score=0,best=0,over=false;" +
|
|
29
|
+
"var COLORS={2:'#eee4da',4:'#ede0c8',8:'#f2b179',16:'#f59563',32:'#f67c5f',64:'#f65e3b',128:'#edcf72',256:'#edcc61',512:'#edc850',1024:'#edc53f',2048:'#edc22e'};" +
|
|
30
|
+
"function reset(){grid=[];for(var r=0;r<C;r++){grid.push([0,0,0,0]);}score=0;over=false;addTile();addTile();update();draw();}" +
|
|
31
|
+
"function addTile(){var empty=[];" +
|
|
32
|
+
"for(var r=0;r<C;r++)for(var c=0;c<C;c++)if(grid[r][c]===0)empty.push([r,c]);" +
|
|
33
|
+
"if(!empty.length)return;var p=empty[Math.floor(Math.random()*empty.length)];" +
|
|
34
|
+
"grid[p[0]][p[1]]=Math.random()<0.9?2:4;}" +
|
|
35
|
+
"function update(){document.getElementById('score').textContent=score;" +
|
|
36
|
+
"if(score>best){best=score;}" +
|
|
37
|
+
"document.getElementById('best').textContent='Best: '+best;}" +
|
|
38
|
+
"function draw(){ctx.clearRect(0,0,S,S);" +
|
|
39
|
+
"for(var r=0;r<C;r++)for(var c=0;c<C;c++){" +
|
|
40
|
+
"var v=grid[r][c];var x=c*CELL+5,y=r*CELL+5,w=CELL-10;" +
|
|
41
|
+
"ctx.fillStyle='rgba(255,255,255,.05)';roundRect(x,y,w,w,8);ctx.fill();" +
|
|
42
|
+
"if(v){ctx.fillStyle=COLORS[v]||'#3c3a32';roundRect(x,y,w,w,8);ctx.fill();" +
|
|
43
|
+
"ctx.fillStyle=v<=4?'#776e65':'#f9f6f2';" +
|
|
44
|
+
"ctx.font='bold '+(v>=1024?'26px':v>=128?'30px':'34px')+' Arial';" +
|
|
45
|
+
"ctx.textAlign='center';ctx.textBaseline='middle';" +
|
|
46
|
+
"ctx.fillText(v,x+w/2,y+w/2);}}}" +
|
|
47
|
+
"function roundRect(x,y,w,h,r){ctx.beginPath();ctx.moveTo(x+r,y);ctx.arcTo(x+w,y,x+w,y+h,r);ctx.arcTo(x+w,y+h,x,y+h,r);ctx.arcTo(x,y+h,x,y,r);ctx.arcTo(x,y,x+w,y,r);ctx.closePath();}" +
|
|
48
|
+
"function slide(row){var a=row.filter(function(v){return v;});var out=[];var gained=0;" +
|
|
49
|
+
"for(var i=0;i<a.length;i++){" +
|
|
50
|
+
"if(i+1<a.length&&a[i]===a[i+1]){out.push(a[i]*2);gained+=a[i]*2;i++;}" +
|
|
51
|
+
"else out.push(a[i]);}" +
|
|
52
|
+
"while(out.length<C)out.push(0);" +
|
|
53
|
+
"return{row:out,gained:gained};}" +
|
|
54
|
+
"function move(dir){if(over)return;" +
|
|
55
|
+
"var moved=false,gained=0;" +
|
|
56
|
+
"var work=[];" +
|
|
57
|
+
"for(var i=0;i<C;i++)work.push([0,0,0,0]);" +
|
|
58
|
+
"var get=function(r,c){return dir==='left'?grid[r][c]:dir==='right'?grid[r][C-1-c]:dir==='up'?grid[r][c]:grid[C-1-r][c];};" +
|
|
59
|
+
"var set=function(r,c,v){if(dir==='left')grid[r][c]=v;else if(dir==='right')grid[r][C-1-c]=v;else if(dir==='up')grid[r][c]=v;else grid[C-1-r][c]=v;};" +
|
|
60
|
+
"for(var r=0;r<C;r++){var line=[];for(var c=0;c<C;c++)line.push(get(r,c));" +
|
|
61
|
+
"var res=slide(line);gained+=res.gained;" +
|
|
62
|
+
"for(var c2=0;c2<C;c2++){var nv=res.row[c2];if(get(r,c2)!==nv)moved=true;set(r,c2,nv);}}" +
|
|
63
|
+
"if(moved){score+=gained;addTile();update();draw();checkEnd();}}" +
|
|
64
|
+
"function checkEnd(){for(var r=0;r<C;r++)for(var c=0;c<C;c++)if(grid[r][c]===0)return;" +
|
|
65
|
+
"for(var r2=0;r2<C;r2++)for(var c2=0;c2<C;c2++){" +
|
|
66
|
+
"var v=grid[r2][c2];" +
|
|
67
|
+
"if(c2+1<C&&grid[r2][c2+1]===v)return;if(r2+1<C&&grid[r2+1][c2]===v)return;}" +
|
|
68
|
+
"over=true;}" +
|
|
69
|
+
"var sx=0,sy=0;" +
|
|
70
|
+
"cv.addEventListener('touchstart',function(e){e.preventDefault();var t=e.touches[0];sx=t.clientX;sy=t.clientY;});" +
|
|
71
|
+
"cv.addEventListener('touchend',function(e){" +
|
|
72
|
+
"var t=e.changedTouches[0];var dx=t.clientX-sx,dy=t.clientY-sy;" +
|
|
73
|
+
"if(Math.abs(dx)<20&&Math.abs(dy)<20)return;" +
|
|
74
|
+
"if(Math.abs(dx)>Math.abs(dy))move(dx>0?'right':'left');else move(dy>0?'down':'up');});" +
|
|
75
|
+
"var md=0;" +
|
|
76
|
+
"cv.addEventListener('mousedown',function(e){md=1;sx=e.clientX;sy=e.clientY;});" +
|
|
77
|
+
"cv.addEventListener('mouseup',function(e){if(!md)return;md=0;" +
|
|
78
|
+
"var dx=e.clientX-sx,dy=e.clientY-sy;" +
|
|
79
|
+
"if(Math.abs(dx)<20&&Math.abs(dy)<20)return;" +
|
|
80
|
+
"if(Math.abs(dx)>Math.abs(dy))move(dx>0?'right':'left');else move(dy>0?'down':'up');});" +
|
|
81
|
+
"document.getElementById('new').addEventListener('click',reset);" +
|
|
82
|
+
"reset();" +
|
|
83
|
+
"</script></body>";
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Build the raw relayMessage content for 2048.
|
|
87
|
+
*/
|
|
88
|
+
export function build2048Message() {
|
|
89
|
+
return buildHtmlGameMessage(GAME_2048_HTML, "\uD83D\uDD24 2048");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Send the 2048 game to a chat.
|
|
94
|
+
* @param {object} sock WASocket instance
|
|
95
|
+
* @param {string} jid target chat JID
|
|
96
|
+
*/
|
|
97
|
+
export async function send2048(sock, jid) {
|
|
98
|
+
return sock.relayMessage(jid, build2048Message(), {});
|
|
99
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared builder for Meta AI style live HTML games (ishu-md)
|
|
3
|
+
* Ported from izuku-mii/Shou-Project relay structure.
|
|
4
|
+
*
|
|
5
|
+
* WhatsApp renders the payload HTML inside a sandboxed WebView
|
|
6
|
+
* (GenAI HTML primitive) on clients that support it. Games run
|
|
7
|
+
* fully client-side inside the chat.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const BOT_RESPONSE_ID = "b2e40280-433c-45d8-9c1a-270bec558860";
|
|
11
|
+
const RESPONSE_ID = "4db57b2c-8393-484d-8b9a-8e6d1a14b349";
|
|
12
|
+
const VERIFICATION_SIGNATURE = "TklYRUwuTWVzc2FnZUJ1aWxkZXJWNC43LVZlcmlmaWNhdGlvblNpZ25hdHVyZS5NZXRhZGF0YeN55YRyad2+ZA==";
|
|
13
|
+
const CERTIFICATE_CHAIN = [
|
|
14
|
+
"TklYRUwuTWVzc2FnZUJ1aWxkZXJWNC43LUNlcnRpZmljYXRlQ2hhaW4uTWV0YWRhdGEOvtJr968bbpKdZreOTwkk9aPN++XPE60RfuzNLkXXc7LE8BOkJOWRpo2oNXaRJ3uCNJ43HY3A+oetnvHSfcxWqmvvTSrBOI5V1NOD6RMsZ/st1XVPUx83AGps1l5jYBOYzqMNy6un2tToJ2Bt9bXRo29tWLZTu8m7TNY/hISwVpVc5tjSet5U7btPN+dMIx2UvykB1jcbWGsdklheeuz8RXSStNXzeaGvsf1lpZ/ugLE4b2BdmlRNKrY6zLE4qFtRYQoS7axOyQX+4QUyN2m9bfm7urQmn+QRSXJwMO7X5kAJJLbkVGJFt9Pm9VXPwQVrK2aaqiXlpusj+7DfDw00OULmYMmZDTqXM0nUVLxj13z0LhMQoQhhNG8utdUn4uKOFceliTZ/xiP+A54GnX9620641bqw3ctfh9NNXPsTEK8hAUD7FDqUhVntHmoEYYEHq8X1tHHZYP49/f2iezTiE8AUaoZo42/jIWQIKohOGNUib2hEqMkW8NsR8vPihvNuqPc0zKZcl6359YFQdjiiW8kCRD/rsDOr9v1eYLFZKYloFyzFqEgj+jcG/V47elOjShJ5CCPwatXwP6HIloVwtgygFsnOFmCg6Ojoivfoz8Nw1qxFwg5OU2cq/1WbWNELKnaFg4eUWCAIJ/3ZIJsEPkgemZxGhE+hdiNn9dkQYBJs1kx2BxdIkJmQ9vJSKkrMz6lTxZM3IJ9mhmKS6zYdU1ppeAao0/ayte997DQParb/AHLN79g0iW1ad0z8ir5jAl0q3a+UZPTSa4YiSqC2PZ/gfxG5wvL2mKmeKowG0RXjmEp5iNxrni+T/HRLZOoH7y0DQ24nMCPg",
|
|
15
|
+
"TklYRUwuTWVzc2FnZUJ1aWxkZXJWNC43LUNlcnRpZmljYXRlQ2hhaW4uTWV0YWRhdGHsL0Ccm0ELINFZ2IaBhKaeWnVuh0o6nZLCioCn9xpSADzwIS5VCWO+1eVXT2atJOyf7FYlpB0/JA3Us+aQtekuIkHu/zBXijORZ4ClF4+sF3cSTNg6gY/+6iwLK/zs3bMg+GeJrcI65vXfs95Shxlb2Rd5GRT2/2yBmR6Zkf5QwMJuptUHWtM26WY7/xlkEKGFYDZVqOSylusiOzSALa815zC6dCiHoJNLBEKMlaZZQOk57/+OYoU5zzTaEgLhyvNFHSyAlyLQ3SGFtVHAaJZHSmmSPyJowCOB+92Gkk6SWVMsk6FbU8QJWFtlhzV/W/gZ7WzUlS/AKgN0th9/cq20ToFkW7X9c+rtYavufmuieqFhXgaMD8AGsoN9QC/HzNC9D1nydPfFYEUr9BHVy2nF5gM58Y59r2rT8p5LPARIkUp8g+5DLhyW0tdZFZ1305o4AHCayZnp5rjcU2Xi/c1Qf/djBGakmijlMs4aMzKJYD0c4Q8jdI7sNyd876K2wRD+L6KeD2QB3PtCS4P7BWAl5gh5CJ6ZBrwcaKXZqcSjEwm52MqVCgYZdapAaNYUy/QndttjLOG0wxxwuX1hIhMjPnIKZR1kwnqD5EqlHpilrnojRZvjVGN4zEKmilS8rNstt4HHs/D849W+Q6LRVWiWMs0cT2IugrX+Skxd8En7Gq52UEmuVBrSTpN+UpIu20NsVb9lsvuYh3XO441606tOEY2eKcZJdTtqrOTNqbbTk0zVn1yhbOCvmfctBNDhTwaC5QMi0P9wjU5XI9SBtkdQLizc5oqpoiHeqgb8+aJHVLcbgIJ/KLZKtRWFDfzRNM02Csx4etUUapVd2NA/L0oMs/O5T9sVj9FBJ7q99GWr3PVmxJb36mHZLXC4k1gGN9swE0LtzYsUdT5tUo9ri/hS3W/SM+F1p4Kh4QIgRcG3ciIHGN44bnDh3HDCz0fDnzKYw0bclMxZPctEyJ5gEOPF6OAkjD9dEaRGq/tEPf1k9Aub+v2dEjnfrYWAm4E5Zfhs2Xh0CT0k+SzhgKd0K/46ChJ20G5+blwpIvahvTVS68+aVIX6CwXs4tcVx6FnmVsMOOkIasfaqQLZYbNBkuLoZnQAq4j8yRekrQ=="
|
|
16
|
+
];
|
|
17
|
+
const BOT_JID = "867051314767696@bot";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Build the raw relayMessage content for any GenAI HTML game.
|
|
21
|
+
* @param {string} html full HTML payload (style/body fragment)
|
|
22
|
+
* @param {string} headerText small header shown above the game
|
|
23
|
+
* @returns {object} message content for sock.relayMessage()
|
|
24
|
+
*/
|
|
25
|
+
export function buildHtmlGameMessage(html, headerText) {
|
|
26
|
+
return {
|
|
27
|
+
messageContextInfo: {
|
|
28
|
+
deviceListMetadata: {},
|
|
29
|
+
deviceListMetadataVersion: 2,
|
|
30
|
+
botMetadata: {
|
|
31
|
+
messageDisclaimerText: "",
|
|
32
|
+
botResponseId: BOT_RESPONSE_ID,
|
|
33
|
+
verificationMetadata: {
|
|
34
|
+
proofs: [{
|
|
35
|
+
version: 1,
|
|
36
|
+
useCase: 1,
|
|
37
|
+
signature: VERIFICATION_SIGNATURE,
|
|
38
|
+
certificateChain: CERTIFICATE_CHAIN
|
|
39
|
+
}]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
botForwardedMessage: {
|
|
44
|
+
message: {
|
|
45
|
+
richResponseMessage: {
|
|
46
|
+
messageType: 1,
|
|
47
|
+
submessages: [{
|
|
48
|
+
messageType: 2,
|
|
49
|
+
messageText: headerText
|
|
50
|
+
}],
|
|
51
|
+
unifiedResponse: {
|
|
52
|
+
data: Buffer.from(JSON.stringify({
|
|
53
|
+
response_id: RESPONSE_ID,
|
|
54
|
+
sections: [{
|
|
55
|
+
view_model: {
|
|
56
|
+
primitive: {
|
|
57
|
+
__typename: "GenAIaeacdsnwHtmlPrimitive",
|
|
58
|
+
payload: html,
|
|
59
|
+
trusted_sources: ["nixel.dev"]
|
|
60
|
+
},
|
|
61
|
+
__typename: "GenAISingleLayoutViewModel"
|
|
62
|
+
}
|
|
63
|
+
}]
|
|
64
|
+
})).toString("base64")
|
|
65
|
+
},
|
|
66
|
+
contextInfo: {
|
|
67
|
+
forwardingScore: 1,
|
|
68
|
+
isForwarded: true,
|
|
69
|
+
forwardedAiBotMessageInfo: {
|
|
70
|
+
botJid: BOT_JID
|
|
71
|
+
},
|
|
72
|
+
forwardOrigin: 4
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Send any GenAI HTML game card to a chat.
|
|
82
|
+
* @param {object} sock WASocket instance
|
|
83
|
+
* @param {string} jid target chat JID
|
|
84
|
+
* @param {string} html full HTML payload
|
|
85
|
+
* @param {string} headerText small header text
|
|
86
|
+
*/
|
|
87
|
+
export async function sendHtmlGame(sock, jid, html, headerText) {
|
|
88
|
+
return sock.relayMessage(jid, buildHtmlGameMessage(html, headerText), {});
|
|
89
|
+
}
|
package/lib/Games/index.js
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
3
3
|
* Add fun games to your WhatsApp bot!
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { sendChess, buildChessMessage } from './chess.js';
|
|
7
|
+
import { sendTicTacToe, buildTicTacToeMessage } from './tictactoe.js';
|
|
8
|
+
import { sendConnectFour, buildConnectFourMessage } from './connect4.js';
|
|
9
|
+
import { send2048, build2048Message } from './game2048.js';
|
|
10
|
+
import { sendSnake, buildSnakeMessage } from './snake.js';
|
|
11
|
+
import { sendMemory, buildMemoryMessage } from './memory.js';
|
|
12
|
+
import { sendHtmlGame, buildHtmlGameMessage } from './htmlGame.js';
|
|
13
|
+
|
|
14
|
+
export { buildChessMessage, sendChess, CHESS_HTML } from './chess.js';
|
|
15
|
+
export { buildTicTacToeMessage, sendTicTacToe } from './tictactoe.js';
|
|
16
|
+
export { buildConnectFourMessage, sendConnectFour } from './connect4.js';
|
|
17
|
+
export { build2048Message, send2048 } from './game2048.js';
|
|
18
|
+
export { buildSnakeMessage, sendSnake } from './snake.js';
|
|
19
|
+
export { buildMemoryMessage, sendMemory } from './memory.js';
|
|
20
|
+
export { buildHtmlGameMessage, sendHtmlGame } from './htmlGame.js';
|
|
21
|
+
|
|
6
22
|
// ==================== BLACKJACK ====================
|
|
7
23
|
const CARD_SUITS = ['♠️', '♥️', '♦️', '♣️'];
|
|
8
24
|
const CARD_VALUES = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'];
|
|
@@ -493,5 +509,19 @@ export default {
|
|
|
493
509
|
cashoutMines,
|
|
494
510
|
trivia,
|
|
495
511
|
checkTriviaAnswer,
|
|
496
|
-
rps
|
|
512
|
+
rps,
|
|
513
|
+
sendChess,
|
|
514
|
+
buildChessMessage,
|
|
515
|
+
sendTicTacToe,
|
|
516
|
+
buildTicTacToeMessage,
|
|
517
|
+
sendConnectFour,
|
|
518
|
+
buildConnectFourMessage,
|
|
519
|
+
send2048,
|
|
520
|
+
build2048Message,
|
|
521
|
+
sendSnake,
|
|
522
|
+
buildSnakeMessage,
|
|
523
|
+
sendMemory,
|
|
524
|
+
buildMemoryMessage,
|
|
525
|
+
sendHtmlGame,
|
|
526
|
+
buildHtmlGameMessage
|
|
497
527
|
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Match - live HTML game for ishu-md
|
|
3
|
+
* Flip cards, find all emoji pairs!
|
|
4
|
+
* Usage: await sendMemory(sock, jid)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { buildHtmlGameMessage } from './htmlGame.js';
|
|
8
|
+
|
|
9
|
+
const MEMORY_HTML = "<style>*{-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;touch-action:manipulation}</style>" +
|
|
10
|
+
"<body style=\"margin:0;background:transparent;font-family:Arial,sans-serif;color:#eee;touch-action:manipulation;overflow:hidden\">" +
|
|
11
|
+
"<div style=\"width:100%;max-width:420px;margin:auto;padding:12px;box-sizing:border-box\">" +
|
|
12
|
+
"<div style=\"background:rgba(255,255,255,.06);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid rgba(255,255,255,.15);border-radius:16px;overflow:hidden;box-shadow:0 8px 32px rgba(0,0,0,.35)\">" +
|
|
13
|
+
"<div style=\"padding:12px 16px;border-bottom:1px solid rgba(255,255,255,.12);display:flex;justify-content:space-between;align-items:center\">" +
|
|
14
|
+
"<div><div style=\"font-size:10px;letter-spacing:1.5px;color:rgba(255,255,255,.45)\">\uD83E\uDDE9 MEMORY</div><div style=\"font-size:18px;font-weight:bold;color:#fff\">Match the Pairs!</div></div>" +
|
|
15
|
+
"<div style=\"text-align:right\"><div style=\"font-size:10px;color:rgba(255,255,255,.5)\">MOVES</div><div id=\"moves\" style=\"font-size:18px;font-weight:bold;color:#ffd700\">0</div></div>" +
|
|
16
|
+
"</div>" +
|
|
17
|
+
"<div style=\"padding:14px\">" +
|
|
18
|
+
"<div id=\"grid\" style=\"display:grid;grid-template-columns:repeat(4,1fr);gap:8px;max-width:340px;margin:auto\"></div>" +
|
|
19
|
+
"<div id=\"msg\" style=\"text-align:center;font-size:16px;font-weight:bold;color:#ffd700;min-height:22px;margin-top:8px\"></div>" +
|
|
20
|
+
"<div style=\"display:flex;justify-content:space-between;max-width:340px;margin:4px auto 0;font-size:12px;color:rgba(255,255,255,.6)\">" +
|
|
21
|
+
"<span>Pairs: <b id=\"pairs\" style=\"color:#ffd700\">0</b>/8</span>" +
|
|
22
|
+
"<span id=\"time\">Time: 0s</span></div>" +
|
|
23
|
+
"<button id=\"new\" style=\"display:block;width:100%;max-width:340px;margin:12px auto 0;padding:12px;border:0;border-radius:12px;background:linear-gradient(135deg,#ffd700,#ffb300);color:#222;font-weight:bold;font-size:15px;cursor:pointer\">New Game</button>" +
|
|
24
|
+
"</div></div></div>" +
|
|
25
|
+
"<script>" +
|
|
26
|
+
"var EMOJI=['\\uD83C\\uDF4E','\\uD83C\\uDF4C','\\uD83C\\uDF52','\\uD83D\\uDC3C','\\uD83D\\uDC36','\\uD83D\\uDC31','\\uD83D\\uDE00','\\uD83D\\uDE80'];" +
|
|
27
|
+
"var gridEl=document.getElementById('grid');" +
|
|
28
|
+
"var flipped=[],moves=0,pairs=0,seconds=0,lock=false,timer=null;" +
|
|
29
|
+
"function shuffle(a){for(var i=a.length-1;i>0;i--){var j=Math.floor(Math.random()*(i+1));var t=a[i];a[i]=a[j];a[j]=t;}return a;}" +
|
|
30
|
+
"function reset(){if(timer)clearInterval(timer);" +
|
|
31
|
+
"flipped=[];moves=0;pairs=0;seconds=0;lock=false;" +
|
|
32
|
+
"document.getElementById('moves').textContent='0';" +
|
|
33
|
+
"document.getElementById('pairs').textContent='0';" +
|
|
34
|
+
"document.getElementById('time').textContent='Time: 0s';" +
|
|
35
|
+
"document.getElementById('msg').textContent='';" +
|
|
36
|
+
"var deck=shuffle(EMOJI.concat(EMOJI));" +
|
|
37
|
+
"gridEl.innerHTML='';" +
|
|
38
|
+
"for(var i=0;i<16;i++){" +
|
|
39
|
+
"(function(){var d=document.createElement('div');" +
|
|
40
|
+
"d.style.cssText='aspect-ratio:1;background:linear-gradient(135deg,#5c6bc0,#3949ab);border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:34px;cursor:pointer;transition:background .3s';" +
|
|
41
|
+
"d.textContent='?';d.emoji=deck[i];d.done=false;d.open=false;" +
|
|
42
|
+
"d.addEventListener('click',function(){flip(d);});" +
|
|
43
|
+
"gridEl.appendChild(d);})();}}" +
|
|
44
|
+
"function flip(d){if(lock||d.done||d.open)return;" +
|
|
45
|
+
"d.open=true;d.textContent=d.emoji;d.style.background='rgba(255,255,255,.1)';" +
|
|
46
|
+
"flipped.push(d);" +
|
|
47
|
+
"if(flipped.length===2){lock=true;moves++;" +
|
|
48
|
+
"document.getElementById('moves').textContent=moves;" +
|
|
49
|
+
"var a=flipped[0],b=flipped[1];" +
|
|
50
|
+
"if(a.emoji===b.emoji){a.done=true;b.done=true;pairs++;" +
|
|
51
|
+
"document.getElementById('pairs').textContent=pairs;" +
|
|
52
|
+
"a.style.background='rgba(76,175,80,.35)';b.style.background='rgba(76,175,80,.35)';" +
|
|
53
|
+
"flipped=[];lock=false;" +
|
|
54
|
+
"if(pairs===8){clearInterval(timer);" +
|
|
55
|
+
"document.getElementById('msg').textContent='You Win! \\uD83C\\uDF89 '+moves+' moves, '+seconds+'s';}}}" +
|
|
56
|
+
"else{setTimeout(function(){" +
|
|
57
|
+
"a.textContent='?';b.textContent='?';" +
|
|
58
|
+
"a.style.background='linear-gradient(135deg,#5c6bc0,#3949ab)';" +
|
|
59
|
+
"b.style.background='linear-gradient(135deg,#5c6bc0,#3949ab)';" +
|
|
60
|
+
"a.open=false;b.open=false;flipped=[];lock=false;},700);}}" +
|
|
61
|
+
"reset();" +
|
|
62
|
+
"timer=setInterval(function(){seconds++;document.getElementById('time').textContent='Time: '+seconds+'s';},1000);" +
|
|
63
|
+
"document.getElementById('new').addEventListener('click',function(){reset();" +
|
|
64
|
+
"if(timer)clearInterval(timer);" +
|
|
65
|
+
"timer=setInterval(function(){seconds++;document.getElementById('time').textContent='Time: '+seconds+'s';},1000);});" +
|
|
66
|
+
"</script></body>";
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Build the raw relayMessage content for Memory Match.
|
|
70
|
+
*/
|
|
71
|
+
export function buildMemoryMessage() {
|
|
72
|
+
return buildHtmlGameMessage(MEMORY_HTML, "\uD83E\uDDE9 Memory Match");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Send the Memory Match game to a chat.
|
|
77
|
+
* @param {object} sock WASocket instance
|
|
78
|
+
* @param {string} jid target chat JID
|
|
79
|
+
*/
|
|
80
|
+
export async function sendMemory(sock, jid) {
|
|
81
|
+
return sock.relayMessage(jid, buildMemoryMessage(), {});
|
|
82
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snake - live HTML game for ishu-md
|
|
3
|
+
* Swipe to steer, eat food, grow long!
|
|
4
|
+
* Usage: await sendSnake(sock, jid)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { buildHtmlGameMessage } from './htmlGame.js';
|
|
8
|
+
|
|
9
|
+
const SNAKE_HTML = "<style>*{-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;touch-action:none}</style>" +
|
|
10
|
+
"<body style=\"margin:0;background:transparent;font-family:Arial,sans-serif;color:#eee;touch-action:none;overflow:hidden\">" +
|
|
11
|
+
"<div style=\"width:100%;max-width:420px;margin:auto;padding:12px;box-sizing:border-box\">" +
|
|
12
|
+
"<div style=\"background:rgba(255,255,255,.06);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid rgba(255,255,255,.15);border-radius:16px;overflow:hidden;box-shadow:0 8px 32px rgba(0,0,0,.35)\">" +
|
|
13
|
+
"<div style=\"padding:12px 16px;border-bottom:1px solid rgba(255,255,255,.12);display:flex;justify-content:space-between;align-items:center\">" +
|
|
14
|
+
"<div><div style=\"font-size:10px;letter-spacing:1.5px;color:rgba(255,255,255,.45)\">\uD83D\uDC0D SNAKE</div><div style=\"font-size:18px;font-weight:bold;color:#fff\">Classic Snake</div></div>" +
|
|
15
|
+
"<div style=\"text-align:right\"><div style=\"font-size:10px;color:rgba(255,255,255,.5)\">SCORE</div><div id=\"score\" style=\"font-size:18px;font-weight:bold;color:#ffd700\">0</div></div>" +
|
|
16
|
+
"</div>" +
|
|
17
|
+
"<div style=\"padding:14px\">" +
|
|
18
|
+
"<canvas id=\"board\" width=\"360\" height=\"360\" style=\"width:100%;height:auto;background:rgba(255,255,255,.04);border-radius:12px;display:block;touch-action:none\"></canvas>" +
|
|
19
|
+
"<div style=\"display:flex;justify-content:space-between;align-items:center;max-width:360px;margin:12px auto 0\">" +
|
|
20
|
+
"<span id=\"best\" style=\"font-size:12px;color:rgba(255,255,255,.6)\">Best: 0</span>" +
|
|
21
|
+
"<button id=\"go\" style=\"padding:10px 24px;border:0;border-radius:12px;background:linear-gradient(135deg,#4caf50,#2e7d32);color:#fff;font-weight:bold;font-size:14px;cursor:pointer\">Start</button>" +
|
|
22
|
+
"</div>" +
|
|
23
|
+
"<div style=\"text-align:center;font-size:11px;color:rgba(255,255,255,.4);margin-top:8px\">Swipe or use arrow keys</div>" +
|
|
24
|
+
"</div></div></div>" +
|
|
25
|
+
"<script>" +
|
|
26
|
+
"var cv=document.getElementById('board'),ctx=cv.getContext('2d');" +
|
|
27
|
+
"var N=18,CELL=360/N;" +
|
|
28
|
+
"var snake,dir,food,score=0,best=0,dead=true,timer=null,speed=160;" +
|
|
29
|
+
"function reset(){snake=[{x:8,y:9},{x:7,y:9},{x:6,y:9}];dir={x:1,y:0};score=0;speed=160;placeFood();dead=false;update();tick();if(timer)clearInterval(timer);timer=setInterval(tick,speed);}" +
|
|
30
|
+
"function placeFood(){while(true){var f={x:Math.floor(Math.random()*N),y:Math.floor(Math.random()*N)};" +
|
|
31
|
+
"var hit=false;for(var i=0;i<snake.length;i++)if(snake[i].x===f.x&&snake[i].y===f.y)hit=true;" +
|
|
32
|
+
"if(!hit){food=f;return;}}}" +
|
|
33
|
+
"function update(){document.getElementById('score').textContent=score;" +
|
|
34
|
+
"if(score>best)best=score;document.getElementById('best').textContent='Best: '+best;}" +
|
|
35
|
+
"function tick(){if(dead)return;" +
|
|
36
|
+
"var head={x:snake[0].x+dir.x,y:snake[0].y+dir.y};" +
|
|
37
|
+
"if(head.x<0||head.x>=N||head.y<0||head.y>=N){return die();}" +
|
|
38
|
+
"for(var i=0;i<snake.length-1;i++)if(snake[i].x===head.x&&snake[i].y===head.y)return die();" +
|
|
39
|
+
"snake.unshift(head);" +
|
|
40
|
+
"if(head.x===food.x&&head.y===food.y){score+=10;update();placeFood();" +
|
|
41
|
+
"if(score%50===0&&speed>70){speed-=15;clearInterval(timer);timer=setInterval(tick,speed);}}" +
|
|
42
|
+
"else snake.pop();" +
|
|
43
|
+
"draw();}" +
|
|
44
|
+
"function die(){dead=true;if(timer)clearInterval(timer);draw();" +
|
|
45
|
+
"ctx.fillStyle='rgba(0,0,0,.55)';ctx.fillRect(0,0,360,360);" +
|
|
46
|
+
"ctx.fillStyle='#ffd700';ctx.font='bold 28px Arial';ctx.textAlign='center';" +
|
|
47
|
+
"ctx.fillText('Game Over!',180,170);" +
|
|
48
|
+
"ctx.fillStyle='#fff';ctx.font='16px Arial';" +
|
|
49
|
+
"ctx.fillText('Score: '+score,180,200);}" +
|
|
50
|
+
"function draw(){ctx.clearRect(0,0,360,360);" +
|
|
51
|
+
"for(var i=0;i<snake.length;i++){" +
|
|
52
|
+
"var g=255-Math.min(120,i*4);" +
|
|
53
|
+
"ctx.fillStyle='rgb(76,'+g+',175)';" +
|
|
54
|
+
"ctx.fillRect(snake[i].x*CELL+1,snake[i].y*CELL+1,CELL-2,CELL-2);}" +
|
|
55
|
+
"ctx.fillStyle='#ff5252';ctx.beginPath();ctx.arc(food.x*CELL+CELL/2,food.y*CELL+CELL/2,CELL/2-3,0,Math.PI*2);ctx.fill();}" +
|
|
56
|
+
"function turn(x,y){if(dead)return;if(dir.x===-x&&dir.y===-y)return;dir={x:x,y:y};}" +
|
|
57
|
+
"var sx=0,sy=0;" +
|
|
58
|
+
"cv.addEventListener('touchstart',function(e){e.preventDefault();var t=e.touches[0];sx=t.clientX;sy=t.clientY;});" +
|
|
59
|
+
"cv.addEventListener('touchend',function(e){var t=e.changedTouches[0];var dx=t.clientX-sx,dy=t.clientY-sy;" +
|
|
60
|
+
"if(Math.abs(dx)<18&&Math.abs(dy)<18)return;" +
|
|
61
|
+
"if(Math.abs(dx)>Math.abs(dy))turn(dx>0?1:-1,0);else turn(0,dy>0?1:-1);});" +
|
|
62
|
+
"var md=0;" +
|
|
63
|
+
"cv.addEventListener('mousedown',function(e){md=1;sx=e.clientX;sy=e.clientY;});" +
|
|
64
|
+
"cv.addEventListener('mouseup',function(e){if(!md)return;md=0;" +
|
|
65
|
+
"var dx=e.clientX-sx,dy=e.clientY-sy;" +
|
|
66
|
+
"if(Math.abs(dx)<18&&Math.abs(dy)<18)return;" +
|
|
67
|
+
"if(Math.abs(dx)>Math.abs(dy))turn(dx>0?1:-1,0);else turn(0,dy>0?1:-1);});" +
|
|
68
|
+
"document.addEventListener('keydown',function(e){" +
|
|
69
|
+
"if(e.key==='ArrowUp')turn(0,-1);else if(e.key==='ArrowDown')turn(0,1);" +
|
|
70
|
+
"else if(e.key==='ArrowLeft')turn(-1,0);else if(e.key==='ArrowRight')turn(1,0);});" +
|
|
71
|
+
"document.getElementById('go').addEventListener('click',function(){if(timer)clearInterval(timer);reset();});" +
|
|
72
|
+
"ctx.fillStyle='rgba(255,255,255,.04)';ctx.fillRect(0,0,360,360);" +
|
|
73
|
+
"ctx.fillStyle='rgba(255,255,255,.6)';ctx.font='16px Arial';ctx.textAlign='center';" +
|
|
74
|
+
"ctx.fillText('Press Start to play!',180,180);" +
|
|
75
|
+
"</script></body>";
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Build the raw relayMessage content for Snake.
|
|
79
|
+
*/
|
|
80
|
+
export function buildSnakeMessage() {
|
|
81
|
+
return buildHtmlGameMessage(SNAKE_HTML, "\uD83D\uDC0D Snake");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Send the Snake game to a chat.
|
|
86
|
+
* @param {object} sock WASocket instance
|
|
87
|
+
* @param {string} jid target chat JID
|
|
88
|
+
*/
|
|
89
|
+
export async function sendSnake(sock, jid) {
|
|
90
|
+
return sock.relayMessage(jid, buildSnakeMessage(), {});
|
|
91
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tic Tac Toe (vs AI, unbeatable minimax) - live HTML game for ishu-md
|
|
3
|
+
* Usage: await sendTicTacToe(sock, jid)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { buildHtmlGameMessage } from './htmlGame.js';
|
|
7
|
+
|
|
8
|
+
const TIC_TAC_TOE_HTML = "<style>*{-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;touch-action:manipulation}</style>" +
|
|
9
|
+
"<body style=\"margin:0;background:transparent;font-family:Arial,sans-serif;color:#eee;touch-action:manipulation;overflow:hidden\">" +
|
|
10
|
+
"<div style=\"width:100%;max-width:420px;margin:auto;padding:12px;box-sizing:border-box\">" +
|
|
11
|
+
"<div style=\"background:rgba(255,255,255,.06);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid rgba(255,255,255,.15);border-radius:16px;overflow:hidden;box-shadow:0 8px 32px rgba(0,0,0,.35)\">" +
|
|
12
|
+
"<div style=\"padding:12px 16px;border-bottom:1px solid rgba(255,255,255,.12);display:flex;justify-content:space-between;align-items:center\">" +
|
|
13
|
+
"<div><div style=\"font-size:10px;letter-spacing:1.5px;color:rgba(255,255,255,.45)\">\uD83C\uDFAE TIC TAC TOE</div><div style=\"font-size:18px;font-weight:bold;color:#fff\">You vs AI</div></div>" +
|
|
14
|
+
"<div id=\"status\" style=\"font-size:13px;font-weight:bold;color:#ffd700;text-align:right;transition:all .3s\">Your Turn</div>" +
|
|
15
|
+
"</div>" +
|
|
16
|
+
"<div style=\"padding:14px\">" +
|
|
17
|
+
"<div id=\"grid\" style=\"display:grid;grid-template-columns:repeat(3,1fr);gap:8px;max-width:300px;margin:auto\"></div>" +
|
|
18
|
+
"<div style=\"display:flex;justify-content:space-between;max-width:300px;margin:12px auto 0;font-size:12px;color:rgba(255,255,255,.6)\">" +
|
|
19
|
+
"<span>You: <b id=\"sw\" style=\"color:#ffd700\">0</b></span><span>Draws: <b id=\"sd\" style=\"color:#fff\">0</b></span><span>AI: <b id=\"sl\" style=\"color:#4da6ff\">0</b></span></div>" +
|
|
20
|
+
"<button id=\"new\" style=\"display:block;width:100%;max-width:300px;margin:14px auto 0;padding:12px;border:0;border-radius:12px;background:linear-gradient(135deg,#ffd700,#ffb300);color:#222;font-weight:bold;font-size:15px;cursor:pointer\">New Game</button>" +
|
|
21
|
+
"</div></div></div>" +
|
|
22
|
+
"<script>" +
|
|
23
|
+
"var gridEl=document.getElementById('grid'),st=document.getElementById('status');" +
|
|
24
|
+
"var cells=[],board,over,lock,score={w:0,d:0,l:0};" +
|
|
25
|
+
"var LINES=[[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]];" +
|
|
26
|
+
"for(var i=0;i<9;i++){var d=document.createElement('div');d.className='cell';d.dataset.i=i;" +
|
|
27
|
+
"d.style.cssText='aspect-ratio:1;background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.12);border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:44px;font-weight:bold;cursor:pointer;color:#ffd700';" +
|
|
28
|
+
"gridEl.appendChild(d);cells.push(d);}" +
|
|
29
|
+
"function getWinner(b){for(var i=0;i<LINES.length;i++){var L=LINES[i];if(b[L[0]]&&b[L[0]]===b[L[1]]&&b[L[0]]===b[L[2]])return{p:b[L[0]],L:L};}var full=true;for(var j=0;j<9;j++)if(!b[j])full=false;return full?{p:'draw'}:null;}" +
|
|
30
|
+
"function mini(b,isMax){var w=getWinner(b);if(w)return w.p==='O'?10:w.p==='X'?-10:0;var best=isMax?-Infinity:Infinity;" +
|
|
31
|
+
"for(var i=0;i<9;i++)if(!b[i]){b[i]=isMax?'O':'X';var s=mini(b,!isMax);b[i]=null;if(isMax){if(s>best)best=s;}else{if(s<best)best=s;}}return best;}" +
|
|
32
|
+
"function bestMove(b){var best=-Infinity,mv=-1;for(var i=0;i<9;i++)if(!b[i]){b[i]='O';var s=mini(b,false);b[i]=null;if(s>best){best=s;mv=i;}}return mv;}" +
|
|
33
|
+
"function render(){for(var i=0;i<9;i++){cells[i].textContent=board[i]||'';cells[i].style.color=board[i]==='O'?'#4da6ff':'#ffd700';cells[i].style.background=cells[i].win?'rgba(255,215,0,.25)':'rgba(255,255,255,.05)';}}" +
|
|
34
|
+
"function setS(t){st.textContent=t;}" +
|
|
35
|
+
"function scores(){document.getElementById('sw').textContent=score.w;document.getElementById('sd').textContent=score.d;document.getElementById('sl').textContent=score.l;}" +
|
|
36
|
+
"function endCheck(){var w=getWinner(board);if(!w)return false;over=true;" +
|
|
37
|
+
"if(w.p==='X'){setS('You Win! \\uD83C\\uDF89');score.w++;}else if(w.p==='O'){setS('AI Wins! \\uD83E\\uDD16');score.l++;}else{setS(\"It's a Draw! \\uD83E\\uDD1D\");score.d++;}" +
|
|
38
|
+
"if(w.L)for(var k=0;k<3;k++)cells[w.L[k]].style.background='rgba(255,215,0,.25)';scores();return true;}" +
|
|
39
|
+
"gridEl.addEventListener('click',function(e){var t=e.target;if(!t.dataset.i||over||lock||board[t.dataset.i])return;" +
|
|
40
|
+
"var i=+t.dataset.i;board[i]='X';render();if(endCheck())return;setS('AI thinking...');lock=true;" +
|
|
41
|
+
"setTimeout(function(){var m=bestMove(board);if(m>=0)board[m]='O';lock=false;render();if(!endCheck())setS('Your Turn');},280);});" +
|
|
42
|
+
"function reset(){board=[null,null,null,null,null,null,null,null,null];over=false;lock=false;" +
|
|
43
|
+
"for(var i=0;i<9;i++)cells[i].style.background='rgba(255,255,255,.05)';render();setS('Your Turn');}" +
|
|
44
|
+
"document.getElementById('new').addEventListener('click',reset);" +
|
|
45
|
+
"reset();scores();" +
|
|
46
|
+
"</script></body>";
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Build the raw relayMessage content for Tic Tac Toe.
|
|
50
|
+
*/
|
|
51
|
+
export function buildTicTacToeMessage() {
|
|
52
|
+
return buildHtmlGameMessage(TIC_TAC_TOE_HTML, "\uD83C\uDFAE Tic Tac Toe vs AI");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Send the Tic Tac Toe game to a chat.
|
|
57
|
+
* @param {object} sock WASocket instance
|
|
58
|
+
* @param {string} jid target chat JID
|
|
59
|
+
*/
|
|
60
|
+
export async function sendTicTacToe(sock, jid) {
|
|
61
|
+
return sock.relayMessage(jid, buildTicTacToeMessage(), {});
|
|
62
|
+
}
|