feat: add 2048 minigame

This commit is contained in:
Fd
2026-09-03 18:02:36 +07:00
parent 1ef8fa3908
commit 6574a7fd17
6 changed files with 530 additions and 16 deletions
+29
View File
@@ -0,0 +1,29 @@
package minigame
import (
"fmt"
"os"
hc "neo/context"
"neo/core"
)
func init() { core.Default.Register(Game2048) }
var Game2048 = &core.Command{
Name: "2048",
Aliases: []string{"2048"},
Category: "minigame",
Description: "Play 2048 in WhatsApp",
Run: func(ctx *hc.Ctx) {
htmlBytes, err := os.ReadFile("cmds/minigame/2048.html")
if err != nil {
ctx.Reply(fmt.Sprintf("Failed to read HTML file: %v", err))
return
}
_, err = ctx.SendInteractiveHTML(string(htmlBytes))
if err != nil {
ctx.Reply(fmt.Sprintf("Failed to send game: %v", err))
}
},
}
+394
View File
@@ -0,0 +1,394 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
<title>2048</title>
<style>
html, body {
margin: 0;
padding: 0;
width: 100%;
overflow: hidden;
background: #faf8ef;
font-family: 'Clear Sans', 'Helvetica Neue', Arial, sans-serif;
color: #776e65;
touch-action: manipulation;
}
.game-wrapper {
width: 100%;
max-width: 450px;
margin: auto;
padding: 15px;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
margin-bottom: 15px;
}
.header h1 {
margin: 0;
font-size: 36px;
}
.score-container {
background: #bbada0;
padding: 8px 15px;
border-radius: 5px;
color: white;
font-weight: bold;
font-size: 18px;
text-align: center;
}
.btn-reset {
background: #8f7a66;
color: white;
border: none;
border-radius: 5px;
padding: 8px 15px;
font-weight: bold;
cursor: pointer;
font-size: 16px;
margin-top: 5px;
}
.btn-reset:active {
background: #776e65;
}
#game-container {
background: #bbada0;
border-radius: 10px;
position: relative;
width: 100%;
aspect-ratio: 1;
overflow: hidden;
box-sizing: border-box;
padding: 1%;
}
.cell-bg, .tile {
width: 25%;
height: 25%;
position: absolute;
padding: 1.5%;
box-sizing: border-box;
top: 0;
left: 0;
}
.cell-bg-inner {
width: 100%;
height: 100%;
background: rgba(238, 228, 218, 0.35);
border-radius: 5px;
}
.tile {
z-index: 10;
}
.tile.new .tile-inner {
animation: appear 0.2s;
}
.tile.merged .tile-inner {
animation: pop 0.2s;
}
@keyframes appear {
0% { opacity: 0; transform: scale(0); }
100% { opacity: 1; transform: scale(1); }
}
@keyframes pop {
0% { transform: scale(1); }
50% { transform: scale(1.2); }
100% { transform: scale(1); }
}
.tile-inner {
width: 100%;
height: 100%;
border-radius: 5px;
display: flex;
justify-content: center;
align-items: center;
font-size: 28px;
font-weight: bold;
}
.tile-2 .tile-inner { background: #eee4da; color: #776e65; }
.tile-4 .tile-inner { background: #ede0c8; color: #776e65; }
.tile-8 .tile-inner { background: #f2b179; color: #f9f6f2; }
.tile-16 .tile-inner { background: #f59563; color: #f9f6f2; }
.tile-32 .tile-inner { background: #f67c5f; color: #f9f6f2; }
.tile-64 .tile-inner { background: #f65e3b; color: #f9f6f2; }
.tile-128 .tile-inner { background: #edcf72; color: #f9f6f2; font-size: 24px; }
.tile-256 .tile-inner { background: #edcc61; color: #f9f6f2; font-size: 24px; }
.tile-512 .tile-inner { background: #edc850; color: #f9f6f2; font-size: 24px; }
.tile-1024 .tile-inner { background: #edc53f; color: #f9f6f2; font-size: 20px; }
.tile-2048 .tile-inner { background: #edc22e; color: #f9f6f2; font-size: 20px; }
#game-over {
display: none;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(238, 228, 218, 0.73);
border-radius: 10px;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 100;
}
#game-over button {
margin-top: 20px;
padding: 10px 20px;
background: #8f7a66;
color: white;
border: none;
border-radius: 5px;
font-size: 18px;
font-weight: bold;
cursor: pointer;
}
.controls {
width: 100%;
max-width: 250px;
margin: 30px auto 20px auto;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: 60px 60px;
gap: 15px;
flex-grow: 1;
align-content: end;
}
.controls button {
background: #8f7a66;
color: white;
border: none;
border-radius: 10px;
font-size: 24px;
font-weight: bold;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
user-select: none;
}
.controls button:active {
background: #776e65;
}
.btn-up { grid-column: 2; grid-row: 1; }
.btn-left { grid-column: 1; grid-row: 2; }
.btn-down { grid-column: 2; grid-row: 2; }
.btn-right { grid-column: 3; grid-row: 2; }
</style>
</head>
<body>
<div class="game-wrapper">
<div class="header">
<div>
<h1>2048</h1>
<button class="btn-reset" onclick="confirmReset()">Reset</button>
</div>
<div class="score-container">
<div>Score</div>
<div id="score">0</div>
</div>
</div>
<div id="game-container">
<div id="grid-bg"></div>
<div id="tile-container"></div>
<div id="game-over">
<h2 style="margin:0; font-size: 36px;">Game Over!</h2>
<button onclick="startGame()">Try Again</button>
</div>
</div>
<div class="controls">
<button class="btn-up" onclick="move('Up')">↑</button>
<button class="btn-left" onclick="move('Left')">←</button>
<button class="btn-down" onclick="move('Down')">↓</button>
<button class="btn-right" onclick="move('Right')">→</button>
</div>
</div>
<script>
const gridBg = document.getElementById('grid-bg');
const tileContainer = document.getElementById('tile-container');
const scoreEl = document.getElementById('score');
const gameOverEl = document.getElementById('game-over');
let board = [];
let score = 0;
let tiles = {}; // id -> { value, el, r, c }
let tileIdCounter = 0;
function initGrid() {
gridBg.innerHTML = '';
for (let r = 0; r < 4; r++) {
for (let c = 0; c < 4; c++) {
const bg = document.createElement('div');
bg.className = 'cell-bg';
bg.style.transform = `translate(${c * 100}%, ${r * 100}%)`;
bg.innerHTML = '<div class="cell-bg-inner"></div>';
gridBg.appendChild(bg);
}
}
}
function confirmReset() {
if (confirm("Are you sure you want to reset the game?")) {
startGame();
}
}
function startGame() {
board = Array(4).fill(null).map(() => Array(4).fill(null));
tiles = {};
score = 0;
scoreEl.innerText = score;
gameOverEl.style.display = 'none';
tileContainer.innerHTML = '';
addRandomTile();
addRandomTile();
}
function addRandomTile() {
let empty = [];
for (let r = 0; r < 4; r++) {
for (let c = 0; c < 4; c++) {
if (!board[r][c]) empty.push({r, c});
}
}
if (empty.length > 0) {
let {r, c} = empty[Math.floor(Math.random() * empty.length)];
let value = Math.random() < 0.9 ? 2 : 4;
createTile(r, c, value);
}
}
function createTile(r, c, value) {
let id = ++tileIdCounter;
let el = document.createElement('div');
el.className = `tile tile-${value} new`;
el.style.transition = 'none';
el.style.transform = `translate(${c * 100}%, ${r * 100}%)`;
el.innerHTML = `<div class="tile-inner">${value}</div>`;
tileContainer.appendChild(el);
let tile = { id, value, el, r, c };
tiles[id] = tile;
board[r][c] = tile;
// Remove 'new' class after animation
setTimeout(() => {
el.classList.remove('new');
}, 200);
}
function updateTilePos(tile, r, c) {
tile.r = r;
tile.c = c;
tile.el.style.transition = 'transform 0.15s ease-in-out';
tile.el.style.transform = `translate(${c * 100}%, ${r * 100}%)`;
}
function move(direction) {
let moved = false;
let scoreInc = 0;
// Remove merged class from all tiles
Object.values(tiles).forEach(t => t.el.classList.remove('merged'));
const traverse = (r, c, dr, dc) => {
let curr = board[r][c];
if (!curr) return;
let nr = r + dr, nc = c + dc;
let lastEmpty = null;
while (nr >= 0 && nr < 4 && nc >= 0 && nc < 4) {
if (board[nr][nc]) {
if (board[nr][nc].value === curr.value && !board[nr][nc].mergedThisTurn) {
// Merge
let target = board[nr][nc];
board[r][c] = null;
updateTilePos(curr, nr, nc);
// Animate removal of old tile and update target
setTimeout(() => {
curr.el.remove();
delete tiles[curr.id];
target.value *= 2;
scoreInc += target.value;
target.el.className = `tile tile-${target.value} merged`;
target.el.style.transform = `translate(${target.c * 100}%, ${target.r * 100}%)`;
target.el.innerHTML = `<div class="tile-inner">${target.value}</div>`;
scoreEl.innerText = score + scoreInc;
}, 150);
target.mergedThisTurn = true;
moved = true;
return;
}
break;
}
lastEmpty = {r: nr, c: nc};
nr += dr; nc += dc;
}
if (lastEmpty) {
board[r][c] = null;
board[lastEmpty.r][lastEmpty.c] = curr;
updateTilePos(curr, lastEmpty.r, lastEmpty.c);
moved = true;
}
};
if (direction === 'Left') {
for(let r=0; r<4; r++) for(let c=1; c<4; c++) traverse(r, c, 0, -1);
} else if (direction === 'Right') {
for(let r=0; r<4; r++) for(let c=2; c>=0; c--) traverse(r, c, 0, 1);
} else if (direction === 'Up') {
for(let c=0; c<4; c++) for(let r=1; r<4; r++) traverse(r, c, -1, 0);
} else if (direction === 'Down') {
for(let c=0; c<4; c++) for(let r=2; r>=0; r--) traverse(r, c, 1, 0);
}
if (moved) {
// Clear merge flags
for(let r=0; r<4; r++) for(let c=0; c<4; c++) {
if (board[r][c]) board[r][c].mergedThisTurn = false;
}
score += scoreInc;
setTimeout(() => {
addRandomTile();
if (checkGameOver()) gameOverEl.style.display = 'flex';
}, 150);
}
}
function checkGameOver() {
for (let r = 0; r < 4; r++) {
for (let c = 0; c < 4; c++) {
if (!board[r][c]) return false;
if (r !== 3 && board[r+1][c] && board[r][c].value === board[r+1][c].value) return false;
if (c !== 3 && board[r][c+1] && board[r][c].value === board[r][c+1].value) return false;
}
}
return true;
}
document.addEventListener('keydown', (e) => {
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
e.preventDefault();
move(e.key.replace('Arrow', ''));
}
});
initGrid();
startGame();
</script>
</body>
</html>
+1
View File
@@ -5,5 +5,6 @@ import (
_ "neo/cmds/general" _ "neo/cmds/general"
_ "neo/cmds/group" _ "neo/cmds/group"
_ "neo/cmds/media" _ "neo/cmds/media"
_ "neo/cmds/minigame"
_ "neo/cmds/misc" _ "neo/cmds/misc"
) )
+62 -2
View File
@@ -2,7 +2,12 @@ package context
import ( import (
stdctx "context" stdctx "context"
"encoding/json"
"github.com/google/uuid"
"go.mau.fi/whatsmeow"
"go.mau.fi/whatsmeow/proto/waAICommon"
"go.mau.fi/whatsmeow/proto/waAICommonDeprecated"
waCommon "go.mau.fi/whatsmeow/proto/waCommon" waCommon "go.mau.fi/whatsmeow/proto/waCommon"
waE2E "go.mau.fi/whatsmeow/proto/waE2E" waE2E "go.mau.fi/whatsmeow/proto/waE2E"
waTypes "go.mau.fi/whatsmeow/types" waTypes "go.mau.fi/whatsmeow/types"
@@ -32,8 +37,8 @@ func (c *Ctx) Send(text string) {
c.client.SendMessage(stdctx.Background(), c.event.Info.Chat, msg) c.client.SendMessage(stdctx.Background(), c.event.Info.Chat, msg)
} }
func (c *Ctx) SendMessage(msg *waE2E.Message) { func (c *Ctx) SendMessage(msg *waE2E.Message, extra ...whatsmeow.SendRequestExtra) {
c.client.SendMessage(stdctx.Background(), c.event.Info.Chat, msg) c.client.SendMessage(stdctx.Background(), c.event.Info.Chat, msg, extra...)
} }
func (c *Ctx) React(emoji string) { func (c *Ctx) React(emoji string) {
@@ -104,3 +109,58 @@ func (c *Ctx) SendPresence(composing bool) {
_ = c.client.SubscribePresence(stdctx.Background(), c.event.Info.Chat) _ = c.client.SubscribePresence(stdctx.Background(), c.event.Info.Chat)
_ = c.client.SendChatPresence(stdctx.Background(), c.event.Info.Chat, state, waTypes.ChatPresenceMediaText) _ = c.client.SendChatPresence(stdctx.Background(), c.event.Info.Chat, state, waTypes.ChatPresenceMediaText)
} }
func (c *Ctx) SendInteractiveHTML(htmlContent string) (whatsmeow.SendResponse, error) {
responseID := uuid.New().String()
payload := map[string]interface{}{
"response_id": responseID,
"sections": []map[string]interface{}{
{
"view_model": map[string]interface{}{
"primitive": map[string]interface{}{
"__typename": "GenAIaeacdsnwHtmlPrimitive",
"payload": htmlContent,
"trusted_sources": []string{},
},
"__typename": "GenAISingleLayoutViewModel",
},
},
},
}
data, err := json.Marshal(payload)
if err != nil {
return whatsmeow.SendResponse{}, err
}
msg := &waE2E.Message{
BotForwardedMessage: &waE2E.FutureProofMessage{
Message: &waE2E.Message{
RichResponseMessage: &waE2E.AIRichResponseMessage{
MessageType: waAICommonDeprecated.AIRichResponseMessageType(1).Enum(),
Submessages: []*waAICommonDeprecated.AIRichResponseSubMessage{
{
MessageType: waAICommonDeprecated.AIRichResponseSubMessageType(2).Enum(),
MessageText: proto.String("Game Center"),
},
},
UnifiedResponse: &waAICommon.AIRichResponseUnifiedResponse{
Data: data,
},
ContextInfo: &waE2E.ContextInfo{
ForwardingScore: proto.Uint32(1),
IsForwarded: proto.Bool(true),
ForwardedAiBotMessageInfo: &waAICommon.ForwardedAIBotMessageInfo{
BotJID: proto.String("867051314767696@bot"),
},
},
},
},
},
}
return c.client.SendMessage(stdctx.Background(), c.event.Info.Chat, msg, whatsmeow.SendRequestExtra{
ID: responseID,
})
}
+15 -14
View File
@@ -1,15 +1,15 @@
module neo module neo
go 1.25.5 go 1.26.0
require ( require (
github.com/davidbyttow/govips/v2 v2.18.0 github.com/davidbyttow/govips/v2 v2.18.0
github.com/gabriel-vasile/mimetype v1.4.13 github.com/gabriel-vasile/mimetype v1.4.13
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
github.com/mattn/go-sqlite3 v1.14.48 github.com/mattn/go-sqlite3 v1.14.49
github.com/mdp/qrterminal/v3 v3.2.1 github.com/mdp/qrterminal/v3 v3.2.1
go.mau.fi/whatsmeow v0.0.0-20260630180629-b572e5bcb92b go.mau.fi/whatsmeow v0.0.0-20260828224850-0fadda796019
google.golang.org/protobuf v1.36.11 google.golang.org/protobuf v1.36.12
gorm.io/driver/sqlite v1.6.0 gorm.io/driver/sqlite v1.6.0
gorm.io/gorm v1.31.2 gorm.io/gorm v1.31.2
) )
@@ -18,24 +18,25 @@ require (
filippo.io/edwards25519 v1.2.0 // indirect filippo.io/edwards25519 v1.2.0 // indirect
github.com/beeper/argo-go v1.1.2 // indirect github.com/beeper/argo-go v1.1.2 // indirect
github.com/coder/websocket v1.8.15 // indirect github.com/coder/websocket v1.8.15 // indirect
github.com/elliotchance/orderedmap/v3 v3.1.0 // indirect github.com/elliotchance/orderedmap/v3 v3.1.1 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-colorable v0.1.15 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.24 // indirect
github.com/petermattis/goid v0.0.0-20260330135022-df67b199bc81 // indirect github.com/petermattis/goid v0.0.0-20260816044145-ed329add6b1b // indirect
github.com/rs/zerolog v1.35.1 // indirect github.com/rs/zerolog v1.35.1 // indirect
github.com/vektah/gqlparser/v2 v2.5.27 // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
github.com/vektah/gqlparser/v2 v2.5.37 // indirect
go.mau.fi/libsignal v0.2.2 // indirect go.mau.fi/libsignal v0.2.2 // indirect
go.mau.fi/util v0.9.10 // indirect go.mau.fi/util v0.10.1-0.20260820140024-eb612d936fde // indirect
golang.org/x/crypto v0.54.0 // indirect golang.org/x/crypto v0.56.0 // indirect
golang.org/x/exp v0.0.0-20260611194520-c48552f49976 // indirect golang.org/x/exp v0.0.0-20260813180055-c1d0aacb2297 // indirect
golang.org/x/image v0.44.0 // indirect golang.org/x/image v0.44.0 // indirect
golang.org/x/net v0.57.0 // indirect golang.org/x/net v0.58.0 // indirect
golang.org/x/sync v0.22.0 // indirect golang.org/x/sync v0.22.0 // indirect
golang.org/x/sys v0.47.0 // indirect golang.org/x/sys v0.47.0 // indirect
golang.org/x/term v0.45.0 // indirect golang.org/x/term v0.45.0 // indirect
golang.org/x/text v0.40.0 // indirect golang.org/x/text v0.41.0 // indirect
rsc.io/qr v0.2.0 // indirect rsc.io/qr v0.2.0 // indirect
) )
+29
View File
@@ -16,6 +16,8 @@ github.com/davidbyttow/govips/v2 v2.18.0 h1:pZRshWVYvewP/TZx3yZ7YeC42WyLXg53tHy5
github.com/davidbyttow/govips/v2 v2.18.0/go.mod h1:8+nst5zfMoats12PgmmAPh6p5OfjDaXK0BXMFl/vOcM= github.com/davidbyttow/govips/v2 v2.18.0/go.mod h1:8+nst5zfMoats12PgmmAPh6p5OfjDaXK0BXMFl/vOcM=
github.com/elliotchance/orderedmap/v3 v3.1.0 h1:j4DJ5ObEmMBt/lcwIecKcoRxIQUEnw0L804lXYDt/pg= github.com/elliotchance/orderedmap/v3 v3.1.0 h1:j4DJ5ObEmMBt/lcwIecKcoRxIQUEnw0L804lXYDt/pg=
github.com/elliotchance/orderedmap/v3 v3.1.0/go.mod h1:G+Hc2RwaZvJMcS4JpGCOyViCnGeKf0bTYCGTO4uhjSo= github.com/elliotchance/orderedmap/v3 v3.1.0/go.mod h1:G+Hc2RwaZvJMcS4JpGCOyViCnGeKf0bTYCGTO4uhjSo=
github.com/elliotchance/orderedmap/v3 v3.1.1 h1:eV7lfZ5fVL8d36b8Wogqi/eqm7R/kZcftA9Yiyj+63M=
github.com/elliotchance/orderedmap/v3 v3.1.1/go.mod h1:G+Hc2RwaZvJMcS4JpGCOyViCnGeKf0bTYCGTO4uhjSo=
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
@@ -30,38 +32,61 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY=
github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI=
github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
github.com/mattn/go-sqlite3 v1.14.48 h1:7XHIgl0a8HwOaiK4E47ozLkST78rR9+OtNGx27D/TFs= github.com/mattn/go-sqlite3 v1.14.48 h1:7XHIgl0a8HwOaiK4E47ozLkST78rR9+OtNGx27D/TFs=
github.com/mattn/go-sqlite3 v1.14.48/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w= github.com/mattn/go-sqlite3 v1.14.48/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w=
github.com/mattn/go-sqlite3 v1.14.49 h1:B8jBHC3xhxZgxztrgruTuLucebnULQnx4W7cF7SAE9w=
github.com/mattn/go-sqlite3 v1.14.49/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w=
github.com/mdp/qrterminal/v3 v3.2.1 h1:6+yQjiiOsSuXT5n9/m60E54vdgFsw0zhADHhHLrFet4= github.com/mdp/qrterminal/v3 v3.2.1 h1:6+yQjiiOsSuXT5n9/m60E54vdgFsw0zhADHhHLrFet4=
github.com/mdp/qrterminal/v3 v3.2.1/go.mod h1:jOTmXvnBsMy5xqLniO0R++Jmjs2sTm9dFSuQ5kpz/SU= github.com/mdp/qrterminal/v3 v3.2.1/go.mod h1:jOTmXvnBsMy5xqLniO0R++Jmjs2sTm9dFSuQ5kpz/SU=
github.com/petermattis/goid v0.0.0-20260330135022-df67b199bc81 h1:WDsQxOJDy0N1VRAjXLpi8sCEZRSGarLWQevDxpTBRrM= github.com/petermattis/goid v0.0.0-20260330135022-df67b199bc81 h1:WDsQxOJDy0N1VRAjXLpi8sCEZRSGarLWQevDxpTBRrM=
github.com/petermattis/goid v0.0.0-20260330135022-df67b199bc81/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/petermattis/goid v0.0.0-20260330135022-df67b199bc81/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/petermattis/goid v0.0.0-20260816044145-ed329add6b1b h1:sS7HLzwS+dO+gxATgQfeZDEdUZe2pKAB3nGoUwP5zU0=
github.com/petermattis/goid v0.0.0-20260816044145-ed329add6b1b/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/zerolog v1.35.1 h1:m7xQeoiLIiV0BCEY4Hs+j2NG4Gp2o2KPKmhnnLiazKI= github.com/rs/zerolog v1.35.1 h1:m7xQeoiLIiV0BCEY4Hs+j2NG4Gp2o2KPKmhnnLiazKI=
github.com/rs/zerolog v1.35.1/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw= github.com/rs/zerolog v1.35.1/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
github.com/vektah/gqlparser/v2 v2.5.27 h1:RHPD3JOplpk5mP5JGX8RKZkt2/Vwj/PZv0HxTdwFp0s= github.com/vektah/gqlparser/v2 v2.5.27 h1:RHPD3JOplpk5mP5JGX8RKZkt2/Vwj/PZv0HxTdwFp0s=
github.com/vektah/gqlparser/v2 v2.5.27/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= github.com/vektah/gqlparser/v2 v2.5.27/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo=
github.com/vektah/gqlparser/v2 v2.5.37 h1:jbb1Ilv+xBklV6653tKb4oVUupPNTLb5LmrnBKVI12Y=
github.com/vektah/gqlparser/v2 v2.5.37/go.mod h1:9O4Ox6Ngd3Y12bMD3w6i3CRQXh8W1oC1q0m6olCymDM=
go.mau.fi/libsignal v0.2.2 h1:QV+XdzQkm3x3aSG7FcqfGSZuFXz83pRZPBFaPygHbOU= go.mau.fi/libsignal v0.2.2 h1:QV+XdzQkm3x3aSG7FcqfGSZuFXz83pRZPBFaPygHbOU=
go.mau.fi/libsignal v0.2.2/go.mod h1:CRlIQg2J8uYTfDFvNoO8/KcZjs5cey0vbc6oj/bssY0= go.mau.fi/libsignal v0.2.2/go.mod h1:CRlIQg2J8uYTfDFvNoO8/KcZjs5cey0vbc6oj/bssY0=
go.mau.fi/util v0.9.10 h1:wzvz5iDHyqDXB8vgisD4d3SzucLXNM3iNY+1O1RoHtg= go.mau.fi/util v0.9.10 h1:wzvz5iDHyqDXB8vgisD4d3SzucLXNM3iNY+1O1RoHtg=
go.mau.fi/util v0.9.10/go.mod h1:YQOxySn+ZE3qSYqNxvyX7Yi3suA8YK17PS6QqBREW7A= go.mau.fi/util v0.9.10/go.mod h1:YQOxySn+ZE3qSYqNxvyX7Yi3suA8YK17PS6QqBREW7A=
go.mau.fi/util v0.10.1-0.20260820140024-eb612d936fde h1:eMHY9dMDkNuDMWhfTbMZHbbsxj7G6mfujjKei1HaFQM=
go.mau.fi/util v0.10.1-0.20260820140024-eb612d936fde/go.mod h1:z0ZZNt4hq3FZbUKnunexE/QscCx7VkLvQSvtggc/aE8=
go.mau.fi/whatsmeow v0.0.0-20260630180629-b572e5bcb92b h1:ZUk1ErarDNpnbosXR/MeOz2gkqA4S1bh8zjaSRj7N+Y= go.mau.fi/whatsmeow v0.0.0-20260630180629-b572e5bcb92b h1:ZUk1ErarDNpnbosXR/MeOz2gkqA4S1bh8zjaSRj7N+Y=
go.mau.fi/whatsmeow v0.0.0-20260630180629-b572e5bcb92b/go.mod h1:9dmNTYZ/1pHjPw/bz+azBsGjAkcrZbqzMrKcvG5bJ8U= go.mau.fi/whatsmeow v0.0.0-20260630180629-b572e5bcb92b/go.mod h1:9dmNTYZ/1pHjPw/bz+azBsGjAkcrZbqzMrKcvG5bJ8U=
go.mau.fi/whatsmeow v0.0.0-20260828224850-0fadda796019 h1:vUX4rgZhobwtMYH6tkYOSyfd2ufTysNEDWVpTSVPjmc=
go.mau.fi/whatsmeow v0.0.0-20260828224850-0fadda796019/go.mod h1:aMd13H2xFFGH9cskcvxo4Aae+TmyFN38yw+HvsrpwVg=
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y=
golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I=
golang.org/x/exp v0.0.0-20260611194520-c48552f49976 h1:X8Hz2ImujgbmetVuW+w2YkyZChE3cBpZi2P158rTG9M= golang.org/x/exp v0.0.0-20260611194520-c48552f49976 h1:X8Hz2ImujgbmetVuW+w2YkyZChE3cBpZi2P158rTG9M=
golang.org/x/exp v0.0.0-20260611194520-c48552f49976/go.mod h1:vnf4pv9iKZXY58sQE1L86zmNWJ4159e1RkcWiLCkeEY= golang.org/x/exp v0.0.0-20260611194520-c48552f49976/go.mod h1:vnf4pv9iKZXY58sQE1L86zmNWJ4159e1RkcWiLCkeEY=
golang.org/x/exp v0.0.0-20260813180055-c1d0aacb2297 h1:YXnL44eJ77R+ji4/ooy8UsXIhz+lbi2Qgdlc8iRN0gY=
golang.org/x/exp v0.0.0-20260813180055-c1d0aacb2297/go.mod h1:Mkmymgv+uMpSQ/XxJ/7GpdrdYoqm3u72jEbpCLiJmNk=
golang.org/x/image v0.44.0 h1:+tDekMZED9+LrtB3G5xzRggpVh9CARjZqROla3R3R+I= golang.org/x/image v0.44.0 h1:+tDekMZED9+LrtB3G5xzRggpVh9CARjZqROla3R3R+I=
golang.org/x/image v0.44.0/go.mod h1:V8K3KE9KKKE+pLpQDOeN18w9oacNSvy1tDOirTu4xtY= golang.org/x/image v0.44.0/go.mod h1:V8K3KE9KKKE+pLpQDOeN18w9oacNSvy1tDOirTu4xtY=
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -71,8 +96,12 @@ golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=