@kaikybrofc/omnizap-system 2.1.8
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/.env.example +534 -0
- package/LICENSE +21 -0
- package/README.md +431 -0
- package/RELEASE-v2.1.2.md +83 -0
- package/app/config/adminIdentity.js +87 -0
- package/app/config/baileysConfig.js +693 -0
- package/app/config/groupUtils.js +388 -0
- package/app/connection/socketController.js +992 -0
- package/app/controllers/messageController.js +354 -0
- package/app/modules/adminModule/groupCommandHandlers.js +1294 -0
- package/app/modules/adminModule/groupEventHandlers.js +355 -0
- package/app/modules/aiModule/catCommand.js +1006 -0
- package/app/modules/broadcastModule/noticeCommand.js +416 -0
- package/app/modules/gameModule/diceCommand.js +67 -0
- package/app/modules/menuModule/common.js +311 -0
- package/app/modules/menuModule/menus.js +59 -0
- package/app/modules/playModule/playCommand.js +1615 -0
- package/app/modules/quoteModule/quoteCommand.js +851 -0
- package/app/modules/rpgPokemonModule/rpgBattleCanvasRenderer.js +786 -0
- package/app/modules/rpgPokemonModule/rpgBattleService.js +2082 -0
- package/app/modules/rpgPokemonModule/rpgBattleService.test.js +760 -0
- package/app/modules/rpgPokemonModule/rpgEvolutionUtils.js +22 -0
- package/app/modules/rpgPokemonModule/rpgPokemonCommand.js +172 -0
- package/app/modules/rpgPokemonModule/rpgPokemonDomain.js +192 -0
- package/app/modules/rpgPokemonModule/rpgPokemonDomain.test.js +93 -0
- package/app/modules/rpgPokemonModule/rpgPokemonEvolution.test.js +46 -0
- package/app/modules/rpgPokemonModule/rpgPokemonMessages.js +746 -0
- package/app/modules/rpgPokemonModule/rpgPokemonRepository.js +1859 -0
- package/app/modules/rpgPokemonModule/rpgPokemonService.js +6738 -0
- package/app/modules/rpgPokemonModule/rpgProfileCanvasRenderer.js +354 -0
- package/app/modules/statsModule/globalRankingCommand.js +65 -0
- package/app/modules/statsModule/noMessageCommand.js +288 -0
- package/app/modules/statsModule/rankingCommand.js +60 -0
- package/app/modules/statsModule/rankingCommon.js +889 -0
- package/app/modules/stickerModule/addStickerMetadata.js +239 -0
- package/app/modules/stickerModule/convertToWebp.js +390 -0
- package/app/modules/stickerModule/stickerCommand.js +454 -0
- package/app/modules/stickerModule/stickerConvertCommand.js +156 -0
- package/app/modules/stickerModule/stickerTextCommand.js +657 -0
- package/app/modules/stickerPackModule/autoPackCollectorRuntime.js +20 -0
- package/app/modules/stickerPackModule/autoPackCollectorService.js +284 -0
- package/app/modules/stickerPackModule/semanticReclassificationEngine.js +466 -0
- package/app/modules/stickerPackModule/semanticReclassificationEngine.test.js +88 -0
- package/app/modules/stickerPackModule/semanticThemeClusterService.js +571 -0
- package/app/modules/stickerPackModule/stickerAssetClassificationRepository.js +449 -0
- package/app/modules/stickerPackModule/stickerAssetRepository.js +400 -0
- package/app/modules/stickerPackModule/stickerAssetReprocessQueueRepository.js +180 -0
- package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +4078 -0
- package/app/modules/stickerPackModule/stickerClassificationBackgroundRuntime.js +598 -0
- package/app/modules/stickerPackModule/stickerClassificationService.js +588 -0
- package/app/modules/stickerPackModule/stickerMarketplaceDriftService.js +102 -0
- package/app/modules/stickerPackModule/stickerPackCatalogHttp.js +7506 -0
- package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +1095 -0
- package/app/modules/stickerPackModule/stickerPackEngagementRepository.js +108 -0
- package/app/modules/stickerPackModule/stickerPackErrors.js +30 -0
- package/app/modules/stickerPackModule/stickerPackInteractionEventRepository.js +110 -0
- package/app/modules/stickerPackModule/stickerPackItemRepository.js +440 -0
- package/app/modules/stickerPackModule/stickerPackMarketplaceService.js +337 -0
- package/app/modules/stickerPackModule/stickerPackMessageService.js +296 -0
- package/app/modules/stickerPackModule/stickerPackRepository.js +442 -0
- package/app/modules/stickerPackModule/stickerPackService.js +788 -0
- package/app/modules/stickerPackModule/stickerPackServiceRuntime.js +51 -0
- package/app/modules/stickerPackModule/stickerPackUtils.js +97 -0
- package/app/modules/stickerPackModule/stickerStorageService.js +507 -0
- package/app/modules/stickerPackModule/stickerWorkerPipelineRuntime.js +233 -0
- package/app/modules/stickerPackModule/stickerWorkerTaskQueueRepository.js +205 -0
- package/app/modules/systemMetricsModule/pingCommand.js +421 -0
- package/app/modules/tiktokModule/tiktokCommand.js +798 -0
- package/app/modules/userModule/userCommand.js +1217 -0
- package/app/modules/waifuPicsModule/waifuPicsCommand.js +177 -0
- package/app/observability/metrics.js +734 -0
- package/app/services/captchaService.js +492 -0
- package/app/services/dbWriteQueue.js +572 -0
- package/app/services/groupMetadataService.js +279 -0
- package/app/services/lidMapService.js +663 -0
- package/app/services/messagePersistenceService.js +56 -0
- package/app/services/newsBroadcastService.js +351 -0
- package/app/services/pokeApiService.js +398 -0
- package/app/services/queueUtils.js +57 -0
- package/app/services/socketState.js +7 -0
- package/app/store/aiPromptStore.js +38 -0
- package/app/store/groupConfigStore.js +58 -0
- package/app/store/premiumUserStore.js +36 -0
- package/app/utils/antiLink/antiLinkModule.js +804 -0
- package/app/utils/http/getImageBufferModule.js +18 -0
- package/app/utils/json/jsonSanitizer.js +113 -0
- package/app/utils/json/jsonSanitizer.test.js +40 -0
- package/app/utils/logger/loggerModule.js +262 -0
- package/app/utils/systemMetrics/systemMetricsModule.js +91 -0
- package/database/index.js +2052 -0
- package/database/init.js +516 -0
- package/database/migrations/20260203_0001_sticker_packs.sql +54 -0
- package/database/migrations/20260210_0003_rpg_pokemon.sql +58 -0
- package/database/migrations/20260210_0004_rpg_shiny_biome.sql +9 -0
- package/database/migrations/20260210_0005_rpg_missions.sql +14 -0
- package/database/migrations/20260210_0006_rpg_world_pokedex_traits.sql +27 -0
- package/database/migrations/20260210_0007_rpg_raid_pvp.sql +56 -0
- package/database/migrations/20260210_0008_rpg_social_system.sql +195 -0
- package/database/migrations/20260211_0009_rpg_social_xp.sql +36 -0
- package/database/migrations/20260222_0010_remove_message_xp.sql +2 -0
- package/database/migrations/20260226_0011_sticker_asset_classification.sql +17 -0
- package/database/migrations/20260226_0012_sticker_pack_engagement.sql +16 -0
- package/database/migrations/20260226_0013_sticker_marketplace_intelligence.sql +19 -0
- package/database/migrations/20260226_0014_sticker_pack_publish_flow.sql +30 -0
- package/database/migrations/20260226_0014_sticker_worker_queues.sql +42 -0
- package/database/migrations/20260226_0015_sticker_auto_pack_curation_integrity.sql +18 -0
- package/database/migrations/20260226_0016_sticker_web_google_auth_persistence.sql +34 -0
- package/database/migrations/20260226_0017_sticker_web_admin_ban.sql +22 -0
- package/database/migrations/20260226_0018_sticker_web_admin_moderator.sql +18 -0
- package/database/migrations/20260227_0019_sticker_classification_v2_signals.sql +12 -0
- package/database/migrations/20260227_0020_semantic_theme_clusters.sql +35 -0
- package/docker-compose.yml +103 -0
- package/ecosystem.prod.config.cjs +35 -0
- package/eslint.config.js +61 -0
- package/index.js +437 -0
- package/ml/clip_classifier/Dockerfile +16 -0
- package/ml/clip_classifier/README.md +120 -0
- package/ml/clip_classifier/adaptive_scoring.py +40 -0
- package/ml/clip_classifier/classifier.py +654 -0
- package/ml/clip_classifier/embedding_store.py +481 -0
- package/ml/clip_classifier/env_loader.py +15 -0
- package/ml/clip_classifier/llm_label_expander.py +144 -0
- package/ml/clip_classifier/main.py +213 -0
- package/ml/clip_classifier/requirements.txt +10 -0
- package/ml/clip_classifier/similarity_engine.py +74 -0
- package/observability/alert-rules.yml +60 -0
- package/observability/grafana/dashboards/omnizap-mysql.json +136 -0
- package/observability/grafana/dashboards/omnizap-overview.json +170 -0
- package/observability/grafana/provisioning/dashboards/dashboards.yml +11 -0
- package/observability/grafana/provisioning/datasources/datasources.yml +15 -0
- package/observability/loki-config.yml +38 -0
- package/observability/mysql-exporter.cnf +5 -0
- package/observability/mysql-setup.sql +46 -0
- package/observability/prometheus.yml +32 -0
- package/observability/promtail-config.yml +84 -0
- package/package.json +109 -0
- package/public/api-docs/index.html +144 -0
- package/public/css/github-project-panel.css +297 -0
- package/public/css/stickers-admin.css +1272 -0
- package/public/css/styles.css +671 -0
- package/public/index.html +1311 -0
- package/public/js/apps/apiDocsApp.js +310 -0
- package/public/js/apps/createPackApp.js +2069 -0
- package/public/js/apps/homeApp.js +396 -0
- package/public/js/apps/stickersAdminApp.js +1744 -0
- package/public/js/apps/stickersApp.js +4830 -0
- package/public/js/catalog.js +1019 -0
- package/public/js/github-panel/components/CommitList.js +34 -0
- package/public/js/github-panel/components/ErrorState.js +16 -0
- package/public/js/github-panel/components/GithubProjectPanel.js +106 -0
- package/public/js/github-panel/components/ReleaseList.js +38 -0
- package/public/js/github-panel/components/SkeletonPanel.js +22 -0
- package/public/js/github-panel/components/StatCard.js +15 -0
- package/public/js/github-panel/index.js +15 -0
- package/public/js/github-panel/useGithubRepoData.js +154 -0
- package/public/js/github-panel/vendor/react.js +11 -0
- package/public/js/runtime/react-runtime.js +19 -0
- package/public/licenca/index.html +106 -0
- package/public/stickers/admin/index.html +23 -0
- package/public/stickers/create/index.html +47 -0
- package/public/stickers/index.html +48 -0
- package/public/termos-de-uso/index.html +125 -0
- package/scripts/cache-bust.mjs +107 -0
- package/scripts/deploy.sh +458 -0
- package/scripts/github-deploy-notify.mjs +174 -0
- package/scripts/release.sh +129 -0
|
@@ -0,0 +1,1859 @@
|
|
|
1
|
+
import { executeQuery, TABLES } from '../../../database/index.js';
|
|
2
|
+
|
|
3
|
+
const PLAYER_COLUMNS = 'jid, level, xp, xp_pool_social, gold, created_at, updated_at';
|
|
4
|
+
const PLAYER_POKEMON_COLUMNS = 'id, owner_jid, poke_id, nickname, level, xp, current_hp, ivs_json, moves_json, nature_key, ability_key, ability_name, is_shiny, is_active, created_at';
|
|
5
|
+
const BATTLE_COLUMNS = 'chat_jid, owner_jid, my_pokemon_id, enemy_snapshot_json, turn, expires_at, created_at, updated_at';
|
|
6
|
+
|
|
7
|
+
const parseJson = (value, fallback) => {
|
|
8
|
+
if (value === null || value === undefined) return fallback;
|
|
9
|
+
if (typeof value === 'object') return value;
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(value);
|
|
12
|
+
} catch {
|
|
13
|
+
return fallback;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const normalizePlayerPokemon = (row) => {
|
|
18
|
+
if (!row) return null;
|
|
19
|
+
return {
|
|
20
|
+
...row,
|
|
21
|
+
ivs_json: parseJson(row.ivs_json, {}),
|
|
22
|
+
moves_json: parseJson(row.moves_json, []),
|
|
23
|
+
is_shiny: Number(row.is_shiny) === 1,
|
|
24
|
+
is_active: Number(row.is_active) === 1,
|
|
25
|
+
level: Number(row.level || 1),
|
|
26
|
+
xp: Number(row.xp || 0),
|
|
27
|
+
current_hp: Number(row.current_hp || 0),
|
|
28
|
+
poke_id: Number(row.poke_id || 0),
|
|
29
|
+
nature_key: row.nature_key || null,
|
|
30
|
+
ability_key: row.ability_key || null,
|
|
31
|
+
ability_name: row.ability_name || null,
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const normalizeBattle = (row) => {
|
|
36
|
+
if (!row) return null;
|
|
37
|
+
return {
|
|
38
|
+
...row,
|
|
39
|
+
turn: Number(row.turn || 1),
|
|
40
|
+
enemy_snapshot_json: parseJson(row.enemy_snapshot_json, {}),
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const normalizeRaidState = (row) => {
|
|
45
|
+
if (!row) return null;
|
|
46
|
+
return {
|
|
47
|
+
...row,
|
|
48
|
+
boss_snapshot_json: parseJson(row.boss_snapshot_json, {}),
|
|
49
|
+
max_hp: Number(row.max_hp || 0),
|
|
50
|
+
current_hp: Number(row.current_hp || 0),
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const normalizePvpChallenge = (row) => {
|
|
55
|
+
if (!row) return null;
|
|
56
|
+
return {
|
|
57
|
+
...row,
|
|
58
|
+
battle_snapshot_json: parseJson(row.battle_snapshot_json, {}),
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const getPlayerByJid = async (jid, connection = null) => {
|
|
63
|
+
const rows = await executeQuery(
|
|
64
|
+
`SELECT ${PLAYER_COLUMNS}
|
|
65
|
+
FROM ${TABLES.RPG_PLAYER}
|
|
66
|
+
WHERE jid = ?
|
|
67
|
+
LIMIT 1`,
|
|
68
|
+
[jid],
|
|
69
|
+
connection,
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
return rows?.[0] || null;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const getPlayerByJidForUpdate = async (jid, connection) => {
|
|
76
|
+
const rows = await executeQuery(
|
|
77
|
+
`SELECT ${PLAYER_COLUMNS}
|
|
78
|
+
FROM ${TABLES.RPG_PLAYER}
|
|
79
|
+
WHERE jid = ?
|
|
80
|
+
LIMIT 1
|
|
81
|
+
FOR UPDATE`,
|
|
82
|
+
[jid],
|
|
83
|
+
connection,
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
return rows?.[0] || null;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const createPlayer = async ({ jid, level = 1, xp = 0, gold = 200 }, connection = null) => {
|
|
90
|
+
await executeQuery(
|
|
91
|
+
`INSERT INTO ${TABLES.RPG_PLAYER} (jid, level, xp, xp_pool_social, gold)
|
|
92
|
+
VALUES (?, ?, ?, 0, ?)
|
|
93
|
+
ON DUPLICATE KEY UPDATE jid = VALUES(jid)`,
|
|
94
|
+
[jid, level, xp, gold],
|
|
95
|
+
connection,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
return getPlayerByJid(jid, connection);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export const updatePlayerProgress = async ({ jid, level, xp, gold }, connection = null) => {
|
|
102
|
+
await executeQuery(
|
|
103
|
+
`UPDATE ${TABLES.RPG_PLAYER}
|
|
104
|
+
SET level = ?,
|
|
105
|
+
xp = ?,
|
|
106
|
+
gold = ?,
|
|
107
|
+
updated_at = CURRENT_TIMESTAMP
|
|
108
|
+
WHERE jid = ?`,
|
|
109
|
+
[level, xp, gold, jid],
|
|
110
|
+
connection,
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const updatePlayerSocialXpPool = async ({ jid, xpPoolSocial }, connection = null) => {
|
|
115
|
+
await executeQuery(
|
|
116
|
+
`UPDATE ${TABLES.RPG_PLAYER}
|
|
117
|
+
SET xp_pool_social = ?,
|
|
118
|
+
updated_at = CURRENT_TIMESTAMP
|
|
119
|
+
WHERE jid = ?`,
|
|
120
|
+
[Math.max(0, Number(xpPoolSocial) || 0), jid],
|
|
121
|
+
connection,
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export const updatePlayerGoldOnly = async ({ jid, gold }, connection = null) => {
|
|
126
|
+
await executeQuery(
|
|
127
|
+
`UPDATE ${TABLES.RPG_PLAYER}
|
|
128
|
+
SET gold = ?,
|
|
129
|
+
updated_at = CURRENT_TIMESTAMP
|
|
130
|
+
WHERE jid = ?`,
|
|
131
|
+
[gold, jid],
|
|
132
|
+
connection,
|
|
133
|
+
);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export const createPlayerPokemon = async ({ ownerJid, pokeId, nickname = null, level = 5, xp = 0, currentHp, ivsJson, movesJson, natureKey = null, abilityKey = null, abilityName = null, isShiny = false, isActive = false }, connection = null) => {
|
|
137
|
+
const result = await executeQuery(
|
|
138
|
+
`INSERT INTO ${TABLES.RPG_PLAYER_POKEMON}
|
|
139
|
+
(owner_jid, poke_id, nickname, level, xp, current_hp, ivs_json, moves_json, nature_key, ability_key, ability_name, is_shiny, is_active)
|
|
140
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
141
|
+
[ownerJid, pokeId, nickname, level, xp, currentHp, JSON.stringify(ivsJson || {}), JSON.stringify(movesJson || []), natureKey, abilityKey, abilityName, isShiny ? 1 : 0, isActive ? 1 : 0],
|
|
142
|
+
connection,
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const insertedId = Number(result?.insertId || 0);
|
|
146
|
+
if (!insertedId) return null;
|
|
147
|
+
return getPlayerPokemonById(ownerJid, insertedId, connection);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export const listPlayerPokemons = async (ownerJid, connection = null) => {
|
|
151
|
+
const rows = await executeQuery(
|
|
152
|
+
`SELECT ${PLAYER_POKEMON_COLUMNS}
|
|
153
|
+
FROM ${TABLES.RPG_PLAYER_POKEMON}
|
|
154
|
+
WHERE owner_jid = ?
|
|
155
|
+
ORDER BY is_active DESC, level DESC, id ASC`,
|
|
156
|
+
[ownerJid],
|
|
157
|
+
connection,
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
return (rows || []).map(normalizePlayerPokemon);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export const getPlayerPokemonById = async (ownerJid, pokemonId, connection = null) => {
|
|
164
|
+
const rows = await executeQuery(
|
|
165
|
+
`SELECT ${PLAYER_POKEMON_COLUMNS}
|
|
166
|
+
FROM ${TABLES.RPG_PLAYER_POKEMON}
|
|
167
|
+
WHERE owner_jid = ?
|
|
168
|
+
AND id = ?
|
|
169
|
+
LIMIT 1`,
|
|
170
|
+
[ownerJid, pokemonId],
|
|
171
|
+
connection,
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
return normalizePlayerPokemon(rows?.[0] || null);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
export const getPlayerPokemonByIdForUpdate = async (ownerJid, pokemonId, connection) => {
|
|
178
|
+
const rows = await executeQuery(
|
|
179
|
+
`SELECT ${PLAYER_POKEMON_COLUMNS}
|
|
180
|
+
FROM ${TABLES.RPG_PLAYER_POKEMON}
|
|
181
|
+
WHERE owner_jid = ?
|
|
182
|
+
AND id = ?
|
|
183
|
+
LIMIT 1
|
|
184
|
+
FOR UPDATE`,
|
|
185
|
+
[ownerJid, pokemonId],
|
|
186
|
+
connection,
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
return normalizePlayerPokemon(rows?.[0] || null);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
export const getActivePlayerPokemon = async (ownerJid, connection = null) => {
|
|
193
|
+
const rows = await executeQuery(
|
|
194
|
+
`SELECT ${PLAYER_POKEMON_COLUMNS}
|
|
195
|
+
FROM ${TABLES.RPG_PLAYER_POKEMON}
|
|
196
|
+
WHERE owner_jid = ?
|
|
197
|
+
AND is_active = 1
|
|
198
|
+
ORDER BY id ASC
|
|
199
|
+
LIMIT 1`,
|
|
200
|
+
[ownerJid],
|
|
201
|
+
connection,
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
return normalizePlayerPokemon(rows?.[0] || null);
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
export const getActivePlayerPokemonForUpdate = async (ownerJid, connection) => {
|
|
208
|
+
const rows = await executeQuery(
|
|
209
|
+
`SELECT ${PLAYER_POKEMON_COLUMNS}
|
|
210
|
+
FROM ${TABLES.RPG_PLAYER_POKEMON}
|
|
211
|
+
WHERE owner_jid = ?
|
|
212
|
+
AND is_active = 1
|
|
213
|
+
ORDER BY id ASC
|
|
214
|
+
LIMIT 1
|
|
215
|
+
FOR UPDATE`,
|
|
216
|
+
[ownerJid],
|
|
217
|
+
connection,
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
return normalizePlayerPokemon(rows?.[0] || null);
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export const setActivePokemon = async (ownerJid, pokemonId, connection = null) => {
|
|
224
|
+
await executeQuery(
|
|
225
|
+
`UPDATE ${TABLES.RPG_PLAYER_POKEMON}
|
|
226
|
+
SET is_active = 0
|
|
227
|
+
WHERE owner_jid = ?`,
|
|
228
|
+
[ownerJid],
|
|
229
|
+
connection,
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
const result = await executeQuery(
|
|
233
|
+
`UPDATE ${TABLES.RPG_PLAYER_POKEMON}
|
|
234
|
+
SET is_active = 1
|
|
235
|
+
WHERE owner_jid = ?
|
|
236
|
+
AND id = ?`,
|
|
237
|
+
[ownerJid, pokemonId],
|
|
238
|
+
connection,
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
return Number(result?.affectedRows || 0) > 0;
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
export const updatePlayerPokemonState = async ({ id, ownerJid, level, xp, currentHp, movesJson = null, pokeId = null, nickname, isShiny, natureKey, abilityKey, abilityName }, connection = null) => {
|
|
245
|
+
const fields = ['level = ?', 'xp = ?', 'current_hp = ?'];
|
|
246
|
+
const params = [level, xp, currentHp];
|
|
247
|
+
|
|
248
|
+
if (movesJson !== null) {
|
|
249
|
+
fields.push('moves_json = ?');
|
|
250
|
+
params.push(JSON.stringify(movesJson || []));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (Number.isFinite(Number(pokeId)) && Number(pokeId) > 0) {
|
|
254
|
+
fields.push('poke_id = ?');
|
|
255
|
+
params.push(Number(pokeId));
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (nickname !== undefined) {
|
|
259
|
+
fields.push('nickname = ?');
|
|
260
|
+
params.push(nickname ?? null);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (isShiny !== undefined) {
|
|
264
|
+
fields.push('is_shiny = ?');
|
|
265
|
+
params.push(isShiny ? 1 : 0);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (natureKey !== undefined) {
|
|
269
|
+
fields.push('nature_key = ?');
|
|
270
|
+
params.push(natureKey ?? null);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (abilityKey !== undefined) {
|
|
274
|
+
fields.push('ability_key = ?');
|
|
275
|
+
params.push(abilityKey ?? null);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (abilityName !== undefined) {
|
|
279
|
+
fields.push('ability_name = ?');
|
|
280
|
+
params.push(abilityName ?? null);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
params.push(id, ownerJid);
|
|
284
|
+
|
|
285
|
+
await executeQuery(
|
|
286
|
+
`UPDATE ${TABLES.RPG_PLAYER_POKEMON}
|
|
287
|
+
SET ${fields.join(', ')}
|
|
288
|
+
WHERE id = ?
|
|
289
|
+
AND owner_jid = ?`,
|
|
290
|
+
params,
|
|
291
|
+
connection,
|
|
292
|
+
);
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
export const deleteExpiredBattleStatesByOwner = async (ownerJid, connection = null) => {
|
|
296
|
+
const result = await executeQuery(
|
|
297
|
+
`DELETE FROM ${TABLES.RPG_BATTLE_STATE}
|
|
298
|
+
WHERE owner_jid = ?
|
|
299
|
+
AND expires_at <= UTC_TIMESTAMP()`,
|
|
300
|
+
[ownerJid],
|
|
301
|
+
connection,
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
return Number(result?.affectedRows || 0);
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
export const getBattleStateByOwner = async (ownerJid, connection = null) => {
|
|
308
|
+
const rows = await executeQuery(
|
|
309
|
+
`SELECT ${BATTLE_COLUMNS}
|
|
310
|
+
FROM ${TABLES.RPG_BATTLE_STATE}
|
|
311
|
+
WHERE owner_jid = ?
|
|
312
|
+
LIMIT 1`,
|
|
313
|
+
[ownerJid],
|
|
314
|
+
connection,
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
return normalizeBattle(rows?.[0] || null);
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
export const getBattleStateByOwnerForUpdate = async (ownerJid, connection) => {
|
|
321
|
+
const rows = await executeQuery(
|
|
322
|
+
`SELECT ${BATTLE_COLUMNS}
|
|
323
|
+
FROM ${TABLES.RPG_BATTLE_STATE}
|
|
324
|
+
WHERE owner_jid = ?
|
|
325
|
+
LIMIT 1
|
|
326
|
+
FOR UPDATE`,
|
|
327
|
+
[ownerJid],
|
|
328
|
+
connection,
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
return normalizeBattle(rows?.[0] || null);
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
export const upsertBattleState = async ({ chatJid, ownerJid, myPokemonId, battleSnapshot, turn = 1, expiresAt }, connection = null) => {
|
|
335
|
+
await executeQuery(
|
|
336
|
+
`INSERT INTO ${TABLES.RPG_BATTLE_STATE}
|
|
337
|
+
(chat_jid, owner_jid, my_pokemon_id, enemy_snapshot_json, turn, expires_at)
|
|
338
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
339
|
+
ON DUPLICATE KEY UPDATE
|
|
340
|
+
chat_jid = VALUES(chat_jid),
|
|
341
|
+
owner_jid = VALUES(owner_jid),
|
|
342
|
+
my_pokemon_id = VALUES(my_pokemon_id),
|
|
343
|
+
enemy_snapshot_json = VALUES(enemy_snapshot_json),
|
|
344
|
+
turn = VALUES(turn),
|
|
345
|
+
expires_at = VALUES(expires_at),
|
|
346
|
+
updated_at = CURRENT_TIMESTAMP`,
|
|
347
|
+
[chatJid, ownerJid, myPokemonId, JSON.stringify(battleSnapshot || {}), turn, expiresAt],
|
|
348
|
+
connection,
|
|
349
|
+
);
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
export const deleteBattleStateByOwner = async (ownerJid, connection = null) => {
|
|
353
|
+
await executeQuery(
|
|
354
|
+
`DELETE FROM ${TABLES.RPG_BATTLE_STATE}
|
|
355
|
+
WHERE owner_jid = ?`,
|
|
356
|
+
[ownerJid],
|
|
357
|
+
connection,
|
|
358
|
+
);
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
export const getInventoryItems = async (ownerJid, connection = null) => {
|
|
362
|
+
const rows = await executeQuery(
|
|
363
|
+
`SELECT owner_jid, item_key, quantity, created_at, updated_at
|
|
364
|
+
FROM ${TABLES.RPG_PLAYER_INVENTORY}
|
|
365
|
+
WHERE owner_jid = ?
|
|
366
|
+
ORDER BY item_key ASC`,
|
|
367
|
+
[ownerJid],
|
|
368
|
+
connection,
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
return (rows || []).map((row) => ({
|
|
372
|
+
...row,
|
|
373
|
+
quantity: Number(row.quantity || 0),
|
|
374
|
+
}));
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
export const getInventoryItem = async (ownerJid, itemKey, connection = null) => {
|
|
378
|
+
const rows = await executeQuery(
|
|
379
|
+
`SELECT owner_jid, item_key, quantity, created_at, updated_at
|
|
380
|
+
FROM ${TABLES.RPG_PLAYER_INVENTORY}
|
|
381
|
+
WHERE owner_jid = ?
|
|
382
|
+
AND item_key = ?
|
|
383
|
+
LIMIT 1`,
|
|
384
|
+
[ownerJid, itemKey],
|
|
385
|
+
connection,
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
const row = rows?.[0] || null;
|
|
389
|
+
if (!row) return null;
|
|
390
|
+
return {
|
|
391
|
+
...row,
|
|
392
|
+
quantity: Number(row.quantity || 0),
|
|
393
|
+
};
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
export const getInventoryItemForUpdate = async (ownerJid, itemKey, connection) => {
|
|
397
|
+
const rows = await executeQuery(
|
|
398
|
+
`SELECT owner_jid, item_key, quantity, created_at, updated_at
|
|
399
|
+
FROM ${TABLES.RPG_PLAYER_INVENTORY}
|
|
400
|
+
WHERE owner_jid = ?
|
|
401
|
+
AND item_key = ?
|
|
402
|
+
LIMIT 1
|
|
403
|
+
FOR UPDATE`,
|
|
404
|
+
[ownerJid, itemKey],
|
|
405
|
+
connection,
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
const row = rows?.[0] || null;
|
|
409
|
+
if (!row) return null;
|
|
410
|
+
return {
|
|
411
|
+
...row,
|
|
412
|
+
quantity: Number(row.quantity || 0),
|
|
413
|
+
};
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
export const addInventoryItem = async ({ ownerJid, itemKey, quantity }, connection = null) => {
|
|
417
|
+
await executeQuery(
|
|
418
|
+
`INSERT INTO ${TABLES.RPG_PLAYER_INVENTORY} (owner_jid, item_key, quantity)
|
|
419
|
+
VALUES (?, ?, ?)
|
|
420
|
+
ON DUPLICATE KEY UPDATE quantity = quantity + VALUES(quantity), updated_at = CURRENT_TIMESTAMP`,
|
|
421
|
+
[ownerJid, itemKey, quantity],
|
|
422
|
+
connection,
|
|
423
|
+
);
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
export const consumeInventoryItem = async ({ ownerJid, itemKey, quantity }, connection = null) => {
|
|
427
|
+
const safeQty = Number(quantity);
|
|
428
|
+
if (!Number.isFinite(safeQty) || safeQty <= 0) return false;
|
|
429
|
+
|
|
430
|
+
const result = await executeQuery(
|
|
431
|
+
`UPDATE ${TABLES.RPG_PLAYER_INVENTORY}
|
|
432
|
+
SET quantity = quantity - ?
|
|
433
|
+
WHERE owner_jid = ?
|
|
434
|
+
AND item_key = ?
|
|
435
|
+
AND quantity >= ?`,
|
|
436
|
+
[safeQty, ownerJid, itemKey, safeQty],
|
|
437
|
+
connection,
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
const consumed = Number(result?.affectedRows || 0) > 0;
|
|
441
|
+
if (!consumed) return false;
|
|
442
|
+
|
|
443
|
+
await executeQuery(
|
|
444
|
+
`DELETE FROM ${TABLES.RPG_PLAYER_INVENTORY}
|
|
445
|
+
WHERE owner_jid = ?
|
|
446
|
+
AND item_key = ?
|
|
447
|
+
AND quantity <= 0`,
|
|
448
|
+
[ownerJid, itemKey],
|
|
449
|
+
connection,
|
|
450
|
+
);
|
|
451
|
+
|
|
452
|
+
return true;
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
export const getGroupBiomeByJid = async (groupJid, connection = null) => {
|
|
456
|
+
const rows = await executeQuery(
|
|
457
|
+
`SELECT group_jid, biome_key, created_at, updated_at
|
|
458
|
+
FROM ${TABLES.RPG_GROUP_BIOME}
|
|
459
|
+
WHERE group_jid = ?
|
|
460
|
+
LIMIT 1`,
|
|
461
|
+
[groupJid],
|
|
462
|
+
connection,
|
|
463
|
+
);
|
|
464
|
+
|
|
465
|
+
return rows?.[0] || null;
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
export const upsertGroupBiome = async ({ groupJid, biomeKey }, connection = null) => {
|
|
469
|
+
await executeQuery(
|
|
470
|
+
`INSERT INTO ${TABLES.RPG_GROUP_BIOME} (group_jid, biome_key)
|
|
471
|
+
VALUES (?, ?)
|
|
472
|
+
ON DUPLICATE KEY UPDATE biome_key = VALUES(biome_key), updated_at = CURRENT_TIMESTAMP`,
|
|
473
|
+
[groupJid, biomeKey],
|
|
474
|
+
connection,
|
|
475
|
+
);
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
const MISSION_COLUMNS = 'owner_jid, daily_ref_date, daily_progress_json, daily_claimed_at, weekly_ref_date, weekly_progress_json, weekly_claimed_at, created_at, updated_at';
|
|
479
|
+
const SOCIAL_XP_DAILY_COLUMNS = 'day_ref_date, owner_jid, chat_jid, earned_xp, converted_xp, cap_hits, last_message_hash, last_earned_at, created_at, updated_at';
|
|
480
|
+
|
|
481
|
+
const normalizeMissionRow = (row) => {
|
|
482
|
+
if (!row) return null;
|
|
483
|
+
return {
|
|
484
|
+
...row,
|
|
485
|
+
daily_progress_json: parseJson(row.daily_progress_json, {}),
|
|
486
|
+
weekly_progress_json: parseJson(row.weekly_progress_json, {}),
|
|
487
|
+
};
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
export const getMissionProgressByOwner = async (ownerJid, connection = null) => {
|
|
491
|
+
const rows = await executeQuery(
|
|
492
|
+
`SELECT ${MISSION_COLUMNS}
|
|
493
|
+
FROM ${TABLES.RPG_PLAYER_MISSION_PROGRESS}
|
|
494
|
+
WHERE owner_jid = ?
|
|
495
|
+
LIMIT 1`,
|
|
496
|
+
[ownerJid],
|
|
497
|
+
connection,
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
return normalizeMissionRow(rows?.[0] || null);
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
export const getMissionProgressByOwnerForUpdate = async (ownerJid, connection) => {
|
|
504
|
+
const rows = await executeQuery(
|
|
505
|
+
`SELECT ${MISSION_COLUMNS}
|
|
506
|
+
FROM ${TABLES.RPG_PLAYER_MISSION_PROGRESS}
|
|
507
|
+
WHERE owner_jid = ?
|
|
508
|
+
LIMIT 1
|
|
509
|
+
FOR UPDATE`,
|
|
510
|
+
[ownerJid],
|
|
511
|
+
connection,
|
|
512
|
+
);
|
|
513
|
+
|
|
514
|
+
return normalizeMissionRow(rows?.[0] || null);
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
export const createMissionProgress = async ({ ownerJid, dailyRefDate, dailyProgressJson, weeklyRefDate, weeklyProgressJson }, connection = null) => {
|
|
518
|
+
await executeQuery(
|
|
519
|
+
`INSERT INTO ${TABLES.RPG_PLAYER_MISSION_PROGRESS}
|
|
520
|
+
(owner_jid, daily_ref_date, daily_progress_json, daily_claimed_at, weekly_ref_date, weekly_progress_json, weekly_claimed_at)
|
|
521
|
+
VALUES (?, ?, ?, NULL, ?, ?, NULL)
|
|
522
|
+
ON DUPLICATE KEY UPDATE owner_jid = VALUES(owner_jid)`,
|
|
523
|
+
[ownerJid, dailyRefDate, JSON.stringify(dailyProgressJson || {}), weeklyRefDate, JSON.stringify(weeklyProgressJson || {})],
|
|
524
|
+
connection,
|
|
525
|
+
);
|
|
526
|
+
|
|
527
|
+
return getMissionProgressByOwner(ownerJid, connection);
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
export const updateMissionProgress = async ({ ownerJid, dailyRefDate, dailyProgressJson, dailyClaimedAt, weeklyRefDate, weeklyProgressJson, weeklyClaimedAt }, connection = null) => {
|
|
531
|
+
await executeQuery(
|
|
532
|
+
`UPDATE ${TABLES.RPG_PLAYER_MISSION_PROGRESS}
|
|
533
|
+
SET daily_ref_date = ?,
|
|
534
|
+
daily_progress_json = ?,
|
|
535
|
+
daily_claimed_at = ?,
|
|
536
|
+
weekly_ref_date = ?,
|
|
537
|
+
weekly_progress_json = ?,
|
|
538
|
+
weekly_claimed_at = ?,
|
|
539
|
+
updated_at = CURRENT_TIMESTAMP
|
|
540
|
+
WHERE owner_jid = ?`,
|
|
541
|
+
[dailyRefDate, JSON.stringify(dailyProgressJson || {}), dailyClaimedAt || null, weeklyRefDate, JSON.stringify(weeklyProgressJson || {}), weeklyClaimedAt || null, ownerJid],
|
|
542
|
+
connection,
|
|
543
|
+
);
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
export const upsertPokedexEntry = async ({ ownerJid, pokeId }, connection = null) => {
|
|
547
|
+
const result = await executeQuery(
|
|
548
|
+
`INSERT IGNORE INTO ${TABLES.RPG_PLAYER_POKEDEX} (owner_jid, poke_id)
|
|
549
|
+
VALUES (?, ?)`,
|
|
550
|
+
[ownerJid, pokeId],
|
|
551
|
+
connection,
|
|
552
|
+
);
|
|
553
|
+
|
|
554
|
+
return Number(result?.affectedRows || 0) > 0;
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
export const countPokedexEntries = async (ownerJid, connection = null) => {
|
|
558
|
+
const rows = await executeQuery(
|
|
559
|
+
`SELECT COUNT(*) AS total
|
|
560
|
+
FROM ${TABLES.RPG_PLAYER_POKEDEX}
|
|
561
|
+
WHERE owner_jid = ?`,
|
|
562
|
+
[ownerJid],
|
|
563
|
+
connection,
|
|
564
|
+
);
|
|
565
|
+
|
|
566
|
+
return Number(rows?.[0]?.total || 0);
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
export const listPokedexEntries = async (ownerJid, limit = 12, connection = null) => {
|
|
570
|
+
const safeLimit = Math.max(1, Math.min(50, Number(limit) || 12));
|
|
571
|
+
const rows = await executeQuery(
|
|
572
|
+
`SELECT owner_jid, poke_id, first_captured_at
|
|
573
|
+
FROM ${TABLES.RPG_PLAYER_POKEDEX}
|
|
574
|
+
WHERE owner_jid = ?
|
|
575
|
+
ORDER BY first_captured_at DESC
|
|
576
|
+
LIMIT ${safeLimit}`,
|
|
577
|
+
[ownerJid],
|
|
578
|
+
connection,
|
|
579
|
+
);
|
|
580
|
+
|
|
581
|
+
return (rows || []).map((row) => ({
|
|
582
|
+
...row,
|
|
583
|
+
poke_id: Number(row.poke_id || 0),
|
|
584
|
+
}));
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
export const getTravelStateByOwner = async (ownerJid, connection = null) => {
|
|
588
|
+
const rows = await executeQuery(
|
|
589
|
+
`SELECT owner_jid, region_key, location_key, location_area_key, created_at, updated_at
|
|
590
|
+
FROM ${TABLES.RPG_PLAYER_TRAVEL}
|
|
591
|
+
WHERE owner_jid = ?
|
|
592
|
+
LIMIT 1`,
|
|
593
|
+
[ownerJid],
|
|
594
|
+
connection,
|
|
595
|
+
);
|
|
596
|
+
|
|
597
|
+
return rows?.[0] || null;
|
|
598
|
+
};
|
|
599
|
+
|
|
600
|
+
export const getTravelStateByOwnerForUpdate = async (ownerJid, connection) => {
|
|
601
|
+
const rows = await executeQuery(
|
|
602
|
+
`SELECT owner_jid, region_key, location_key, location_area_key, created_at, updated_at
|
|
603
|
+
FROM ${TABLES.RPG_PLAYER_TRAVEL}
|
|
604
|
+
WHERE owner_jid = ?
|
|
605
|
+
LIMIT 1
|
|
606
|
+
FOR UPDATE`,
|
|
607
|
+
[ownerJid],
|
|
608
|
+
connection,
|
|
609
|
+
);
|
|
610
|
+
|
|
611
|
+
return rows?.[0] || null;
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
export const upsertTravelState = async ({ ownerJid, regionKey = null, locationKey = null, locationAreaKey = null }, connection = null) => {
|
|
615
|
+
await executeQuery(
|
|
616
|
+
`INSERT INTO ${TABLES.RPG_PLAYER_TRAVEL} (owner_jid, region_key, location_key, location_area_key)
|
|
617
|
+
VALUES (?, ?, ?, ?)
|
|
618
|
+
ON DUPLICATE KEY UPDATE
|
|
619
|
+
region_key = VALUES(region_key),
|
|
620
|
+
location_key = VALUES(location_key),
|
|
621
|
+
location_area_key = VALUES(location_area_key),
|
|
622
|
+
updated_at = CURRENT_TIMESTAMP`,
|
|
623
|
+
[ownerJid, regionKey, locationKey, locationAreaKey],
|
|
624
|
+
connection,
|
|
625
|
+
);
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
export const getRaidStateByChat = async (chatJid, connection = null) => {
|
|
629
|
+
const rows = await executeQuery(
|
|
630
|
+
`SELECT chat_jid, created_by_jid, biome_key, boss_snapshot_json, max_hp, current_hp, started_at, ends_at, created_at, updated_at
|
|
631
|
+
FROM ${TABLES.RPG_RAID_STATE}
|
|
632
|
+
WHERE chat_jid = ?
|
|
633
|
+
LIMIT 1`,
|
|
634
|
+
[chatJid],
|
|
635
|
+
connection,
|
|
636
|
+
);
|
|
637
|
+
|
|
638
|
+
return normalizeRaidState(rows?.[0] || null);
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
export const getRaidStateByChatForUpdate = async (chatJid, connection) => {
|
|
642
|
+
const rows = await executeQuery(
|
|
643
|
+
`SELECT chat_jid, created_by_jid, biome_key, boss_snapshot_json, max_hp, current_hp, started_at, ends_at, created_at, updated_at
|
|
644
|
+
FROM ${TABLES.RPG_RAID_STATE}
|
|
645
|
+
WHERE chat_jid = ?
|
|
646
|
+
LIMIT 1
|
|
647
|
+
FOR UPDATE`,
|
|
648
|
+
[chatJid],
|
|
649
|
+
connection,
|
|
650
|
+
);
|
|
651
|
+
|
|
652
|
+
return normalizeRaidState(rows?.[0] || null);
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
export const upsertRaidState = async ({ chatJid, createdByJid, biomeKey = null, bossSnapshot, maxHp, currentHp, startedAt, endsAt }, connection = null) => {
|
|
656
|
+
await executeQuery(
|
|
657
|
+
`INSERT INTO ${TABLES.RPG_RAID_STATE}
|
|
658
|
+
(chat_jid, created_by_jid, biome_key, boss_snapshot_json, max_hp, current_hp, started_at, ends_at)
|
|
659
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
660
|
+
ON DUPLICATE KEY UPDATE
|
|
661
|
+
created_by_jid = VALUES(created_by_jid),
|
|
662
|
+
biome_key = VALUES(biome_key),
|
|
663
|
+
boss_snapshot_json = VALUES(boss_snapshot_json),
|
|
664
|
+
max_hp = VALUES(max_hp),
|
|
665
|
+
current_hp = VALUES(current_hp),
|
|
666
|
+
started_at = VALUES(started_at),
|
|
667
|
+
ends_at = VALUES(ends_at),
|
|
668
|
+
updated_at = CURRENT_TIMESTAMP`,
|
|
669
|
+
[chatJid, createdByJid, biomeKey, JSON.stringify(bossSnapshot || {}), maxHp, currentHp, startedAt, endsAt],
|
|
670
|
+
connection,
|
|
671
|
+
);
|
|
672
|
+
};
|
|
673
|
+
|
|
674
|
+
export const deleteRaidStateByChat = async (chatJid, connection = null) => {
|
|
675
|
+
await executeQuery(
|
|
676
|
+
`DELETE FROM ${TABLES.RPG_RAID_STATE}
|
|
677
|
+
WHERE chat_jid = ?`,
|
|
678
|
+
[chatJid],
|
|
679
|
+
connection,
|
|
680
|
+
);
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
export const deleteExpiredRaidStates = async (connection = null) => {
|
|
684
|
+
const result = await executeQuery(
|
|
685
|
+
`DELETE FROM ${TABLES.RPG_RAID_STATE}
|
|
686
|
+
WHERE ends_at <= UTC_TIMESTAMP()`,
|
|
687
|
+
[],
|
|
688
|
+
connection,
|
|
689
|
+
);
|
|
690
|
+
return Number(result?.affectedRows || 0);
|
|
691
|
+
};
|
|
692
|
+
|
|
693
|
+
export const upsertRaidParticipant = async ({ chatJid, ownerJid }, connection = null) => {
|
|
694
|
+
await executeQuery(
|
|
695
|
+
`INSERT INTO ${TABLES.RPG_RAID_PARTICIPANT} (chat_jid, owner_jid, total_damage)
|
|
696
|
+
VALUES (?, ?, 0)
|
|
697
|
+
ON DUPLICATE KEY UPDATE updated_at = CURRENT_TIMESTAMP`,
|
|
698
|
+
[chatJid, ownerJid],
|
|
699
|
+
connection,
|
|
700
|
+
);
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
export const addRaidParticipantDamage = async ({ chatJid, ownerJid, damage }, connection = null) => {
|
|
704
|
+
await executeQuery(
|
|
705
|
+
`INSERT INTO ${TABLES.RPG_RAID_PARTICIPANT} (chat_jid, owner_jid, total_damage)
|
|
706
|
+
VALUES (?, ?, ?)
|
|
707
|
+
ON DUPLICATE KEY UPDATE total_damage = total_damage + VALUES(total_damage), updated_at = CURRENT_TIMESTAMP`,
|
|
708
|
+
[chatJid, ownerJid, Math.max(0, Number(damage) || 0)],
|
|
709
|
+
connection,
|
|
710
|
+
);
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
export const listRaidParticipants = async (chatJid, connection = null) => {
|
|
714
|
+
const rows = await executeQuery(
|
|
715
|
+
`SELECT chat_jid, owner_jid, total_damage, joined_at, updated_at
|
|
716
|
+
FROM ${TABLES.RPG_RAID_PARTICIPANT}
|
|
717
|
+
WHERE chat_jid = ?
|
|
718
|
+
ORDER BY total_damage DESC, joined_at ASC`,
|
|
719
|
+
[chatJid],
|
|
720
|
+
connection,
|
|
721
|
+
);
|
|
722
|
+
|
|
723
|
+
return (rows || []).map((row) => ({
|
|
724
|
+
...row,
|
|
725
|
+
total_damage: Number(row.total_damage || 0),
|
|
726
|
+
}));
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
export const getRaidParticipant = async (chatJid, ownerJid, connection = null) => {
|
|
730
|
+
const rows = await executeQuery(
|
|
731
|
+
`SELECT chat_jid, owner_jid, total_damage, joined_at, updated_at
|
|
732
|
+
FROM ${TABLES.RPG_RAID_PARTICIPANT}
|
|
733
|
+
WHERE chat_jid = ?
|
|
734
|
+
AND owner_jid = ?
|
|
735
|
+
LIMIT 1`,
|
|
736
|
+
[chatJid, ownerJid],
|
|
737
|
+
connection,
|
|
738
|
+
);
|
|
739
|
+
|
|
740
|
+
const row = rows?.[0] || null;
|
|
741
|
+
if (!row) return null;
|
|
742
|
+
return {
|
|
743
|
+
...row,
|
|
744
|
+
total_damage: Number(row.total_damage || 0),
|
|
745
|
+
};
|
|
746
|
+
};
|
|
747
|
+
|
|
748
|
+
export const deleteRaidParticipantsByChat = async (chatJid, connection = null) => {
|
|
749
|
+
await executeQuery(
|
|
750
|
+
`DELETE FROM ${TABLES.RPG_RAID_PARTICIPANT}
|
|
751
|
+
WHERE chat_jid = ?`,
|
|
752
|
+
[chatJid],
|
|
753
|
+
connection,
|
|
754
|
+
);
|
|
755
|
+
};
|
|
756
|
+
|
|
757
|
+
export const createPvpChallenge = async ({ chatJid = null, challengerJid, opponentJid, status = 'pending', turnJid = null, winnerJid = null, battleSnapshot, startedAt = null, expiresAt }, connection = null) => {
|
|
758
|
+
const result = await executeQuery(
|
|
759
|
+
`INSERT INTO ${TABLES.RPG_PVP_CHALLENGE}
|
|
760
|
+
(chat_jid, challenger_jid, opponent_jid, status, turn_jid, winner_jid, battle_snapshot_json, started_at, expires_at)
|
|
761
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
762
|
+
[chatJid, challengerJid, opponentJid, status, turnJid, winnerJid, JSON.stringify(battleSnapshot || {}), startedAt, expiresAt],
|
|
763
|
+
connection,
|
|
764
|
+
);
|
|
765
|
+
|
|
766
|
+
const insertId = Number(result?.insertId || 0);
|
|
767
|
+
if (!insertId) return null;
|
|
768
|
+
return getPvpChallengeById(insertId, connection);
|
|
769
|
+
};
|
|
770
|
+
|
|
771
|
+
export const getPvpChallengeById = async (challengeId, connection = null) => {
|
|
772
|
+
const rows = await executeQuery(
|
|
773
|
+
`SELECT id, chat_jid, challenger_jid, opponent_jid, status, turn_jid, winner_jid, battle_snapshot_json, started_at, expires_at, created_at, updated_at
|
|
774
|
+
FROM ${TABLES.RPG_PVP_CHALLENGE}
|
|
775
|
+
WHERE id = ?
|
|
776
|
+
LIMIT 1`,
|
|
777
|
+
[challengeId],
|
|
778
|
+
connection,
|
|
779
|
+
);
|
|
780
|
+
|
|
781
|
+
return normalizePvpChallenge(rows?.[0] || null);
|
|
782
|
+
};
|
|
783
|
+
|
|
784
|
+
export const getPvpChallengeByIdForUpdate = async (challengeId, connection) => {
|
|
785
|
+
const rows = await executeQuery(
|
|
786
|
+
`SELECT id, chat_jid, challenger_jid, opponent_jid, status, turn_jid, winner_jid, battle_snapshot_json, started_at, expires_at, created_at, updated_at
|
|
787
|
+
FROM ${TABLES.RPG_PVP_CHALLENGE}
|
|
788
|
+
WHERE id = ?
|
|
789
|
+
LIMIT 1
|
|
790
|
+
FOR UPDATE`,
|
|
791
|
+
[challengeId],
|
|
792
|
+
connection,
|
|
793
|
+
);
|
|
794
|
+
|
|
795
|
+
return normalizePvpChallenge(rows?.[0] || null);
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
export const listOpenPvpChallengesByPlayer = async (ownerJid, connection = null) => {
|
|
799
|
+
const rows = await executeQuery(
|
|
800
|
+
`SELECT id, chat_jid, challenger_jid, opponent_jid, status, turn_jid, winner_jid, battle_snapshot_json, started_at, expires_at, created_at, updated_at
|
|
801
|
+
FROM ${TABLES.RPG_PVP_CHALLENGE}
|
|
802
|
+
WHERE (challenger_jid = ? OR opponent_jid = ?)
|
|
803
|
+
AND status IN ('pending', 'active')
|
|
804
|
+
AND expires_at > UTC_TIMESTAMP()
|
|
805
|
+
ORDER BY id DESC`,
|
|
806
|
+
[ownerJid, ownerJid],
|
|
807
|
+
connection,
|
|
808
|
+
);
|
|
809
|
+
|
|
810
|
+
return (rows || []).map(normalizePvpChallenge);
|
|
811
|
+
};
|
|
812
|
+
|
|
813
|
+
export const getActivePvpChallengeByPlayerForUpdate = async (ownerJid, connection) => {
|
|
814
|
+
const rows = await executeQuery(
|
|
815
|
+
`SELECT id, chat_jid, challenger_jid, opponent_jid, status, turn_jid, winner_jid, battle_snapshot_json, started_at, expires_at, created_at, updated_at
|
|
816
|
+
FROM ${TABLES.RPG_PVP_CHALLENGE}
|
|
817
|
+
WHERE (challenger_jid = ? OR opponent_jid = ?)
|
|
818
|
+
AND status = 'active'
|
|
819
|
+
AND expires_at > UTC_TIMESTAMP()
|
|
820
|
+
ORDER BY id DESC
|
|
821
|
+
LIMIT 1
|
|
822
|
+
FOR UPDATE`,
|
|
823
|
+
[ownerJid, ownerJid],
|
|
824
|
+
connection,
|
|
825
|
+
);
|
|
826
|
+
|
|
827
|
+
return normalizePvpChallenge(rows?.[0] || null);
|
|
828
|
+
};
|
|
829
|
+
|
|
830
|
+
export const updatePvpChallengeState = async ({ id, status, turnJid, winnerJid, battleSnapshot, startedAt, expiresAt }, connection = null) => {
|
|
831
|
+
const fields = ['updated_at = CURRENT_TIMESTAMP'];
|
|
832
|
+
const params = [];
|
|
833
|
+
|
|
834
|
+
if (status !== undefined) {
|
|
835
|
+
fields.push('status = ?');
|
|
836
|
+
params.push(status);
|
|
837
|
+
}
|
|
838
|
+
if (turnJid !== undefined) {
|
|
839
|
+
fields.push('turn_jid = ?');
|
|
840
|
+
params.push(turnJid ?? null);
|
|
841
|
+
}
|
|
842
|
+
if (winnerJid !== undefined) {
|
|
843
|
+
fields.push('winner_jid = ?');
|
|
844
|
+
params.push(winnerJid ?? null);
|
|
845
|
+
}
|
|
846
|
+
if (battleSnapshot !== undefined) {
|
|
847
|
+
fields.push('battle_snapshot_json = ?');
|
|
848
|
+
params.push(JSON.stringify(battleSnapshot || {}));
|
|
849
|
+
}
|
|
850
|
+
if (startedAt !== undefined) {
|
|
851
|
+
fields.push('started_at = ?');
|
|
852
|
+
params.push(startedAt ?? null);
|
|
853
|
+
}
|
|
854
|
+
if (expiresAt !== undefined) {
|
|
855
|
+
fields.push('expires_at = ?');
|
|
856
|
+
params.push(expiresAt);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
params.push(id);
|
|
860
|
+
await executeQuery(
|
|
861
|
+
`UPDATE ${TABLES.RPG_PVP_CHALLENGE}
|
|
862
|
+
SET ${fields.join(', ')}
|
|
863
|
+
WHERE id = ?`,
|
|
864
|
+
params,
|
|
865
|
+
connection,
|
|
866
|
+
);
|
|
867
|
+
};
|
|
868
|
+
|
|
869
|
+
export const expireOldPvpChallenges = async (connection = null) => {
|
|
870
|
+
const result = await executeQuery(
|
|
871
|
+
`UPDATE ${TABLES.RPG_PVP_CHALLENGE}
|
|
872
|
+
SET status = 'expired',
|
|
873
|
+
updated_at = CURRENT_TIMESTAMP
|
|
874
|
+
WHERE status IN ('pending', 'active')
|
|
875
|
+
AND expires_at <= UTC_TIMESTAMP()`,
|
|
876
|
+
[],
|
|
877
|
+
connection,
|
|
878
|
+
);
|
|
879
|
+
return Number(result?.affectedRows || 0);
|
|
880
|
+
};
|
|
881
|
+
|
|
882
|
+
const normalizePvpQueueRow = (row) => {
|
|
883
|
+
if (!row) return null;
|
|
884
|
+
return {
|
|
885
|
+
...row,
|
|
886
|
+
id: Number(row.id || 0),
|
|
887
|
+
matched_challenge_id: row.matched_challenge_id ? Number(row.matched_challenge_id) : null,
|
|
888
|
+
};
|
|
889
|
+
};
|
|
890
|
+
|
|
891
|
+
const normalizeTradeOfferRow = (row) => {
|
|
892
|
+
if (!row) return null;
|
|
893
|
+
return {
|
|
894
|
+
...row,
|
|
895
|
+
id: Number(row.id || 0),
|
|
896
|
+
proposer_offer_json: parseJson(row.proposer_offer_json, {}),
|
|
897
|
+
receiver_offer_json: parseJson(row.receiver_offer_json, {}),
|
|
898
|
+
};
|
|
899
|
+
};
|
|
900
|
+
|
|
901
|
+
const normalizeSocialLinkRow = (row) => {
|
|
902
|
+
if (!row) return null;
|
|
903
|
+
return {
|
|
904
|
+
...row,
|
|
905
|
+
friendship_score: Number(row.friendship_score || 0),
|
|
906
|
+
rivalry_score: Number(row.rivalry_score || 0),
|
|
907
|
+
interactions_count: Number(row.interactions_count || 0),
|
|
908
|
+
};
|
|
909
|
+
};
|
|
910
|
+
|
|
911
|
+
const normalizeWeeklyStatsRow = (row) => {
|
|
912
|
+
if (!row) return null;
|
|
913
|
+
return {
|
|
914
|
+
...row,
|
|
915
|
+
matches_played: Number(row.matches_played || 0),
|
|
916
|
+
wins: Number(row.wins || 0),
|
|
917
|
+
losses: Number(row.losses || 0),
|
|
918
|
+
points: Number(row.points || 0),
|
|
919
|
+
};
|
|
920
|
+
};
|
|
921
|
+
|
|
922
|
+
const normalizeCoopWeeklyRow = (row) => {
|
|
923
|
+
if (!row) return null;
|
|
924
|
+
return {
|
|
925
|
+
...row,
|
|
926
|
+
capture_target: Number(row.capture_target || 0),
|
|
927
|
+
raid_target: Number(row.raid_target || 0),
|
|
928
|
+
capture_progress: Number(row.capture_progress || 0),
|
|
929
|
+
raid_progress: Number(row.raid_progress || 0),
|
|
930
|
+
};
|
|
931
|
+
};
|
|
932
|
+
|
|
933
|
+
const normalizeCoopMemberRow = (row) => {
|
|
934
|
+
if (!row) return null;
|
|
935
|
+
return {
|
|
936
|
+
...row,
|
|
937
|
+
capture_contribution: Number(row.capture_contribution || 0),
|
|
938
|
+
raid_contribution: Number(row.raid_contribution || 0),
|
|
939
|
+
};
|
|
940
|
+
};
|
|
941
|
+
|
|
942
|
+
const normalizeGroupEventRow = (row) => {
|
|
943
|
+
if (!row) return null;
|
|
944
|
+
return {
|
|
945
|
+
...row,
|
|
946
|
+
target_value: Number(row.target_value || 0),
|
|
947
|
+
progress_value: Number(row.progress_value || 0),
|
|
948
|
+
};
|
|
949
|
+
};
|
|
950
|
+
|
|
951
|
+
const normalizeGroupEventMemberRow = (row) => {
|
|
952
|
+
if (!row) return null;
|
|
953
|
+
return {
|
|
954
|
+
...row,
|
|
955
|
+
contribution: Number(row.contribution || 0),
|
|
956
|
+
};
|
|
957
|
+
};
|
|
958
|
+
|
|
959
|
+
const normalizeKarmaProfileRow = (row) => {
|
|
960
|
+
if (!row) return null;
|
|
961
|
+
return {
|
|
962
|
+
...row,
|
|
963
|
+
karma_score: Number(row.karma_score || 0),
|
|
964
|
+
positive_votes: Number(row.positive_votes || 0),
|
|
965
|
+
negative_votes: Number(row.negative_votes || 0),
|
|
966
|
+
};
|
|
967
|
+
};
|
|
968
|
+
|
|
969
|
+
const normalizeSocialXpDailyRow = (row) => {
|
|
970
|
+
if (!row) return null;
|
|
971
|
+
return {
|
|
972
|
+
...row,
|
|
973
|
+
earned_xp: Number(row.earned_xp || 0),
|
|
974
|
+
converted_xp: Number(row.converted_xp || 0),
|
|
975
|
+
cap_hits: Number(row.cap_hits || 0),
|
|
976
|
+
};
|
|
977
|
+
};
|
|
978
|
+
|
|
979
|
+
const buildPairUsers = (jidA, jidB) => {
|
|
980
|
+
const a = String(jidA || '').trim();
|
|
981
|
+
const b = String(jidB || '').trim();
|
|
982
|
+
if (!a || !b || a === b) return null;
|
|
983
|
+
return a < b ? [a, b] : [b, a];
|
|
984
|
+
};
|
|
985
|
+
|
|
986
|
+
const buildPairKey = (jidA, jidB) => {
|
|
987
|
+
const pair = buildPairUsers(jidA, jidB);
|
|
988
|
+
if (!pair) return null;
|
|
989
|
+
return `${pair[0]}::${pair[1]}`;
|
|
990
|
+
};
|
|
991
|
+
|
|
992
|
+
export const expirePvpQueue = async (connection = null) => {
|
|
993
|
+
const result = await executeQuery(
|
|
994
|
+
`UPDATE ${TABLES.RPG_PVP_QUEUE}
|
|
995
|
+
SET status = 'expired',
|
|
996
|
+
updated_at = CURRENT_TIMESTAMP
|
|
997
|
+
WHERE status = 'queued'
|
|
998
|
+
AND expires_at <= UTC_TIMESTAMP()`,
|
|
999
|
+
[],
|
|
1000
|
+
connection,
|
|
1001
|
+
);
|
|
1002
|
+
return Number(result?.affectedRows || 0);
|
|
1003
|
+
};
|
|
1004
|
+
|
|
1005
|
+
export const enqueuePvpQueue = async ({ chatJid, ownerJid, expiresAt }, connection = null) => {
|
|
1006
|
+
await executeQuery(
|
|
1007
|
+
`INSERT INTO ${TABLES.RPG_PVP_QUEUE} (chat_jid, owner_jid, status, matched_challenge_id, expires_at)
|
|
1008
|
+
VALUES (?, ?, 'queued', NULL, ?)
|
|
1009
|
+
ON DUPLICATE KEY UPDATE
|
|
1010
|
+
expires_at = VALUES(expires_at),
|
|
1011
|
+
matched_challenge_id = NULL,
|
|
1012
|
+
updated_at = CURRENT_TIMESTAMP`,
|
|
1013
|
+
[chatJid, ownerJid, expiresAt],
|
|
1014
|
+
connection,
|
|
1015
|
+
);
|
|
1016
|
+
};
|
|
1017
|
+
|
|
1018
|
+
export const getQueuedPvpByOwnerForUpdate = async (chatJid, ownerJid, connection) => {
|
|
1019
|
+
const rows = await executeQuery(
|
|
1020
|
+
`SELECT id, chat_jid, owner_jid, status, matched_challenge_id, expires_at, created_at, updated_at
|
|
1021
|
+
FROM ${TABLES.RPG_PVP_QUEUE}
|
|
1022
|
+
WHERE chat_jid = ?
|
|
1023
|
+
AND owner_jid = ?
|
|
1024
|
+
AND status = 'queued'
|
|
1025
|
+
LIMIT 1
|
|
1026
|
+
FOR UPDATE`,
|
|
1027
|
+
[chatJid, ownerJid],
|
|
1028
|
+
connection,
|
|
1029
|
+
);
|
|
1030
|
+
return normalizePvpQueueRow(rows?.[0] || null);
|
|
1031
|
+
};
|
|
1032
|
+
|
|
1033
|
+
export const listQueuedPvpByChatForUpdate = async (chatJid, limit = 10, connection = null) => {
|
|
1034
|
+
const safeLimit = Math.max(1, Math.min(50, Number(limit) || 10));
|
|
1035
|
+
const rows = await executeQuery(
|
|
1036
|
+
`SELECT id, chat_jid, owner_jid, status, matched_challenge_id, expires_at, created_at, updated_at
|
|
1037
|
+
FROM ${TABLES.RPG_PVP_QUEUE}
|
|
1038
|
+
WHERE chat_jid = ?
|
|
1039
|
+
AND status = 'queued'
|
|
1040
|
+
AND expires_at > UTC_TIMESTAMP()
|
|
1041
|
+
ORDER BY created_at ASC
|
|
1042
|
+
LIMIT ${safeLimit}
|
|
1043
|
+
FOR UPDATE`,
|
|
1044
|
+
[chatJid],
|
|
1045
|
+
connection,
|
|
1046
|
+
);
|
|
1047
|
+
return (rows || []).map(normalizePvpQueueRow);
|
|
1048
|
+
};
|
|
1049
|
+
|
|
1050
|
+
export const listQueuedPvpByChat = async (chatJid, limit = 10, connection = null) => {
|
|
1051
|
+
const safeLimit = Math.max(1, Math.min(50, Number(limit) || 10));
|
|
1052
|
+
const rows = await executeQuery(
|
|
1053
|
+
`SELECT id, chat_jid, owner_jid, status, matched_challenge_id, expires_at, created_at, updated_at
|
|
1054
|
+
FROM ${TABLES.RPG_PVP_QUEUE}
|
|
1055
|
+
WHERE chat_jid = ?
|
|
1056
|
+
AND status = 'queued'
|
|
1057
|
+
AND expires_at > UTC_TIMESTAMP()
|
|
1058
|
+
ORDER BY created_at ASC
|
|
1059
|
+
LIMIT ${safeLimit}`,
|
|
1060
|
+
[chatJid],
|
|
1061
|
+
connection,
|
|
1062
|
+
);
|
|
1063
|
+
return (rows || []).map(normalizePvpQueueRow);
|
|
1064
|
+
};
|
|
1065
|
+
|
|
1066
|
+
export const cancelQueuedPvpByOwner = async (chatJid, ownerJid, connection = null) => {
|
|
1067
|
+
const result = await executeQuery(
|
|
1068
|
+
`UPDATE ${TABLES.RPG_PVP_QUEUE}
|
|
1069
|
+
SET status = 'cancelled',
|
|
1070
|
+
updated_at = CURRENT_TIMESTAMP
|
|
1071
|
+
WHERE chat_jid = ?
|
|
1072
|
+
AND owner_jid = ?
|
|
1073
|
+
AND status = 'queued'`,
|
|
1074
|
+
[chatJid, ownerJid],
|
|
1075
|
+
connection,
|
|
1076
|
+
);
|
|
1077
|
+
return Number(result?.affectedRows || 0);
|
|
1078
|
+
};
|
|
1079
|
+
|
|
1080
|
+
export const markPvpQueueMatchedByIds = async (queueIds = [], challengeId = null, connection = null) => {
|
|
1081
|
+
const ids = (queueIds || []).map((value) => Number(value)).filter((value) => Number.isFinite(value) && value > 0);
|
|
1082
|
+
if (!ids.length) return 0;
|
|
1083
|
+
const placeholders = ids.map(() => '?').join(', ');
|
|
1084
|
+
const result = await executeQuery(
|
|
1085
|
+
`UPDATE ${TABLES.RPG_PVP_QUEUE}
|
|
1086
|
+
SET status = 'matched',
|
|
1087
|
+
matched_challenge_id = ?,
|
|
1088
|
+
updated_at = CURRENT_TIMESTAMP
|
|
1089
|
+
WHERE id IN (${placeholders})`,
|
|
1090
|
+
[challengeId, ...ids],
|
|
1091
|
+
connection,
|
|
1092
|
+
);
|
|
1093
|
+
return Number(result?.affectedRows || 0);
|
|
1094
|
+
};
|
|
1095
|
+
|
|
1096
|
+
export const getLatestFinishedPvpByPlayer = async (ownerJid, connection = null) => {
|
|
1097
|
+
const rows = await executeQuery(
|
|
1098
|
+
`SELECT id, chat_jid, challenger_jid, opponent_jid, status, turn_jid, winner_jid, battle_snapshot_json, started_at, expires_at, created_at, updated_at
|
|
1099
|
+
FROM ${TABLES.RPG_PVP_CHALLENGE}
|
|
1100
|
+
WHERE status = 'finished'
|
|
1101
|
+
AND (challenger_jid = ? OR opponent_jid = ?)
|
|
1102
|
+
ORDER BY updated_at DESC, id DESC
|
|
1103
|
+
LIMIT 1`,
|
|
1104
|
+
[ownerJid, ownerJid],
|
|
1105
|
+
connection,
|
|
1106
|
+
);
|
|
1107
|
+
return normalizePvpChallenge(rows?.[0] || null);
|
|
1108
|
+
};
|
|
1109
|
+
|
|
1110
|
+
export const listRecentFinishedPvpByPlayer = async (ownerJid, limit = 20, connection = null) => {
|
|
1111
|
+
const safeLimit = Math.max(1, Math.min(100, Number(limit) || 20));
|
|
1112
|
+
const rows = await executeQuery(
|
|
1113
|
+
`SELECT id, chat_jid, challenger_jid, opponent_jid, status, turn_jid, winner_jid, battle_snapshot_json, started_at, expires_at, created_at, updated_at
|
|
1114
|
+
FROM ${TABLES.RPG_PVP_CHALLENGE}
|
|
1115
|
+
WHERE status = 'finished'
|
|
1116
|
+
AND (challenger_jid = ? OR opponent_jid = ?)
|
|
1117
|
+
ORDER BY updated_at DESC, id DESC
|
|
1118
|
+
LIMIT ${safeLimit}`,
|
|
1119
|
+
[ownerJid, ownerJid],
|
|
1120
|
+
connection,
|
|
1121
|
+
);
|
|
1122
|
+
return (rows || []).map(normalizePvpChallenge);
|
|
1123
|
+
};
|
|
1124
|
+
|
|
1125
|
+
export const upsertPvpWeeklyStatsDelta = async ({ weekRefDate, ownerJid, matchesPlayedDelta = 0, winsDelta = 0, lossesDelta = 0, pointsDelta = 0 }, connection = null) => {
|
|
1126
|
+
await executeQuery(
|
|
1127
|
+
`INSERT INTO ${TABLES.RPG_PVP_WEEKLY_STATS}
|
|
1128
|
+
(week_ref_date, owner_jid, matches_played, wins, losses, points)
|
|
1129
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
1130
|
+
ON DUPLICATE KEY UPDATE
|
|
1131
|
+
matches_played = GREATEST(0, matches_played + VALUES(matches_played)),
|
|
1132
|
+
wins = GREATEST(0, wins + VALUES(wins)),
|
|
1133
|
+
losses = GREATEST(0, losses + VALUES(losses)),
|
|
1134
|
+
points = GREATEST(0, points + VALUES(points)),
|
|
1135
|
+
updated_at = CURRENT_TIMESTAMP`,
|
|
1136
|
+
[weekRefDate, ownerJid, matchesPlayedDelta, winsDelta, lossesDelta, pointsDelta],
|
|
1137
|
+
connection,
|
|
1138
|
+
);
|
|
1139
|
+
};
|
|
1140
|
+
|
|
1141
|
+
export const getPvpWeeklyStatsByOwner = async (weekRefDate, ownerJid, connection = null) => {
|
|
1142
|
+
const rows = await executeQuery(
|
|
1143
|
+
`SELECT week_ref_date, owner_jid, matches_played, wins, losses, points, created_at, updated_at
|
|
1144
|
+
FROM ${TABLES.RPG_PVP_WEEKLY_STATS}
|
|
1145
|
+
WHERE week_ref_date = ?
|
|
1146
|
+
AND owner_jid = ?
|
|
1147
|
+
LIMIT 1`,
|
|
1148
|
+
[weekRefDate, ownerJid],
|
|
1149
|
+
connection,
|
|
1150
|
+
);
|
|
1151
|
+
return normalizeWeeklyStatsRow(rows?.[0] || null);
|
|
1152
|
+
};
|
|
1153
|
+
|
|
1154
|
+
export const getPvpWeeklyRankByOwner = async (weekRefDate, ownerJid, connection = null) => {
|
|
1155
|
+
const stats = await getPvpWeeklyStatsByOwner(weekRefDate, ownerJid, connection);
|
|
1156
|
+
if (!stats) return null;
|
|
1157
|
+
|
|
1158
|
+
const rows = await executeQuery(
|
|
1159
|
+
`SELECT COUNT(*) + 1 AS rank_position
|
|
1160
|
+
FROM ${TABLES.RPG_PVP_WEEKLY_STATS}
|
|
1161
|
+
WHERE week_ref_date = ?
|
|
1162
|
+
AND (
|
|
1163
|
+
points > ?
|
|
1164
|
+
OR (points = ? AND wins > ?)
|
|
1165
|
+
OR (points = ? AND wins = ? AND matches_played > ?)
|
|
1166
|
+
OR (points = ? AND wins = ? AND matches_played = ? AND owner_jid < ?)
|
|
1167
|
+
)`,
|
|
1168
|
+
[
|
|
1169
|
+
weekRefDate,
|
|
1170
|
+
stats.points,
|
|
1171
|
+
stats.points,
|
|
1172
|
+
stats.wins,
|
|
1173
|
+
stats.points,
|
|
1174
|
+
stats.wins,
|
|
1175
|
+
stats.matches_played,
|
|
1176
|
+
stats.points,
|
|
1177
|
+
stats.wins,
|
|
1178
|
+
stats.matches_played,
|
|
1179
|
+
ownerJid,
|
|
1180
|
+
],
|
|
1181
|
+
connection,
|
|
1182
|
+
);
|
|
1183
|
+
|
|
1184
|
+
const rank = Number(rows?.[0]?.rank_position || 0);
|
|
1185
|
+
return rank > 0 ? rank : null;
|
|
1186
|
+
};
|
|
1187
|
+
|
|
1188
|
+
export const getPvpLifetimeStatsByOwner = async (ownerJid, connection = null) => {
|
|
1189
|
+
const rows = await executeQuery(
|
|
1190
|
+
`SELECT
|
|
1191
|
+
COALESCE(SUM(matches_played), 0) AS matches_played,
|
|
1192
|
+
COALESCE(SUM(wins), 0) AS wins,
|
|
1193
|
+
COALESCE(SUM(losses), 0) AS losses,
|
|
1194
|
+
COALESCE(SUM(points), 0) AS points
|
|
1195
|
+
FROM ${TABLES.RPG_PVP_WEEKLY_STATS}
|
|
1196
|
+
WHERE owner_jid = ?`,
|
|
1197
|
+
[ownerJid],
|
|
1198
|
+
connection,
|
|
1199
|
+
);
|
|
1200
|
+
|
|
1201
|
+
const row = rows?.[0] || {};
|
|
1202
|
+
return {
|
|
1203
|
+
matches_played: Number(row.matches_played || 0),
|
|
1204
|
+
wins: Number(row.wins || 0),
|
|
1205
|
+
losses: Number(row.losses || 0),
|
|
1206
|
+
points: Number(row.points || 0),
|
|
1207
|
+
};
|
|
1208
|
+
};
|
|
1209
|
+
|
|
1210
|
+
export const listPvpWeeklyRanking = async (weekRefDate, limit = 10, connection = null) => {
|
|
1211
|
+
const safeLimit = Math.max(1, Math.min(30, Number(limit) || 10));
|
|
1212
|
+
const rows = await executeQuery(
|
|
1213
|
+
`SELECT week_ref_date, owner_jid, matches_played, wins, losses, points, created_at, updated_at
|
|
1214
|
+
FROM ${TABLES.RPG_PVP_WEEKLY_STATS}
|
|
1215
|
+
WHERE week_ref_date = ?
|
|
1216
|
+
ORDER BY points DESC, wins DESC, matches_played DESC, owner_jid ASC
|
|
1217
|
+
LIMIT ${safeLimit}`,
|
|
1218
|
+
[weekRefDate],
|
|
1219
|
+
connection,
|
|
1220
|
+
);
|
|
1221
|
+
return (rows || []).map(normalizeWeeklyStatsRow);
|
|
1222
|
+
};
|
|
1223
|
+
|
|
1224
|
+
export const getSocialLinkByUsers = async (jidA, jidB, connection = null) => {
|
|
1225
|
+
const pair = buildPairUsers(jidA, jidB);
|
|
1226
|
+
if (!pair) return null;
|
|
1227
|
+
const pairKey = buildPairKey(pair[0], pair[1]);
|
|
1228
|
+
const rows = await executeQuery(
|
|
1229
|
+
`SELECT pair_key, user_a_jid, user_b_jid, friendship_score, rivalry_score, interactions_count, last_interaction_at, created_at, updated_at
|
|
1230
|
+
FROM ${TABLES.RPG_SOCIAL_LINK}
|
|
1231
|
+
WHERE pair_key = ?
|
|
1232
|
+
LIMIT 1`,
|
|
1233
|
+
[pairKey],
|
|
1234
|
+
connection,
|
|
1235
|
+
);
|
|
1236
|
+
return normalizeSocialLinkRow(rows?.[0] || null);
|
|
1237
|
+
};
|
|
1238
|
+
|
|
1239
|
+
export const upsertSocialLinkDelta = async ({ jidA, jidB, friendshipDelta = 0, rivalryDelta = 0, interactionsDelta = 1 }, connection = null) => {
|
|
1240
|
+
const pair = buildPairUsers(jidA, jidB);
|
|
1241
|
+
if (!pair) return;
|
|
1242
|
+
const pairKey = buildPairKey(pair[0], pair[1]);
|
|
1243
|
+
await executeQuery(
|
|
1244
|
+
`INSERT INTO ${TABLES.RPG_SOCIAL_LINK}
|
|
1245
|
+
(pair_key, user_a_jid, user_b_jid, friendship_score, rivalry_score, interactions_count, last_interaction_at)
|
|
1246
|
+
VALUES (?, ?, ?, ?, ?, ?, UTC_TIMESTAMP())
|
|
1247
|
+
ON DUPLICATE KEY UPDATE
|
|
1248
|
+
friendship_score = friendship_score + VALUES(friendship_score),
|
|
1249
|
+
rivalry_score = rivalry_score + VALUES(rivalry_score),
|
|
1250
|
+
interactions_count = interactions_count + VALUES(interactions_count),
|
|
1251
|
+
last_interaction_at = UTC_TIMESTAMP(),
|
|
1252
|
+
updated_at = CURRENT_TIMESTAMP`,
|
|
1253
|
+
[pairKey, pair[0], pair[1], friendshipDelta, rivalryDelta, interactionsDelta],
|
|
1254
|
+
connection,
|
|
1255
|
+
);
|
|
1256
|
+
};
|
|
1257
|
+
|
|
1258
|
+
export const listSocialLinksByOwner = async (ownerJid, mode = 'friendship', limit = 10, connection = null) => {
|
|
1259
|
+
const safeLimit = Math.max(1, Math.min(30, Number(limit) || 10));
|
|
1260
|
+
const orderField = mode === 'rivalry' ? 'rivalry_score' : 'friendship_score';
|
|
1261
|
+
const rows = await executeQuery(
|
|
1262
|
+
`SELECT pair_key, user_a_jid, user_b_jid, friendship_score, rivalry_score, interactions_count, last_interaction_at, created_at, updated_at
|
|
1263
|
+
FROM ${TABLES.RPG_SOCIAL_LINK}
|
|
1264
|
+
WHERE user_a_jid = ?
|
|
1265
|
+
OR user_b_jid = ?
|
|
1266
|
+
ORDER BY ${orderField} DESC, interactions_count DESC, updated_at DESC
|
|
1267
|
+
LIMIT ${safeLimit}`,
|
|
1268
|
+
[ownerJid, ownerJid],
|
|
1269
|
+
connection,
|
|
1270
|
+
);
|
|
1271
|
+
return (rows || []).map(normalizeSocialLinkRow);
|
|
1272
|
+
};
|
|
1273
|
+
|
|
1274
|
+
export const getSocialSummaryByOwner = async (ownerJid, connection = null) => {
|
|
1275
|
+
const rows = await executeQuery(
|
|
1276
|
+
`SELECT
|
|
1277
|
+
COUNT(*) AS links_total,
|
|
1278
|
+
COALESCE(SUM(interactions_count), 0) AS interactions_total,
|
|
1279
|
+
COALESCE(MAX(friendship_score), 0) AS top_friendship,
|
|
1280
|
+
COALESCE(MAX(rivalry_score), 0) AS top_rivalry
|
|
1281
|
+
FROM ${TABLES.RPG_SOCIAL_LINK}
|
|
1282
|
+
WHERE user_a_jid = ?
|
|
1283
|
+
OR user_b_jid = ?`,
|
|
1284
|
+
[ownerJid, ownerJid],
|
|
1285
|
+
connection,
|
|
1286
|
+
);
|
|
1287
|
+
|
|
1288
|
+
const row = rows?.[0] || {};
|
|
1289
|
+
return {
|
|
1290
|
+
linksTotal: Number(row.links_total || 0),
|
|
1291
|
+
interactionsTotal: Number(row.interactions_total || 0),
|
|
1292
|
+
topFriendship: Number(row.top_friendship || 0),
|
|
1293
|
+
topRivalry: Number(row.top_rivalry || 0),
|
|
1294
|
+
};
|
|
1295
|
+
};
|
|
1296
|
+
|
|
1297
|
+
export const createTradeOffer = async ({ chatJid = null, proposerJid, receiverJid, proposerOffer, receiverOffer, expiresAt }, connection = null) => {
|
|
1298
|
+
const result = await executeQuery(
|
|
1299
|
+
`INSERT INTO ${TABLES.RPG_TRADE_OFFER}
|
|
1300
|
+
(chat_jid, proposer_jid, receiver_jid, proposer_offer_json, receiver_offer_json, status, accepted_at, expires_at)
|
|
1301
|
+
VALUES (?, ?, ?, ?, ?, 'pending', NULL, ?)`,
|
|
1302
|
+
[chatJid, proposerJid, receiverJid, JSON.stringify(proposerOffer || {}), JSON.stringify(receiverOffer || {}), expiresAt],
|
|
1303
|
+
connection,
|
|
1304
|
+
);
|
|
1305
|
+
const id = Number(result?.insertId || 0);
|
|
1306
|
+
if (!id) return null;
|
|
1307
|
+
return getTradeOfferById(id, connection);
|
|
1308
|
+
};
|
|
1309
|
+
|
|
1310
|
+
export const getTradeOfferById = async (offerId, connection = null) => {
|
|
1311
|
+
const rows = await executeQuery(
|
|
1312
|
+
`SELECT id, chat_jid, proposer_jid, receiver_jid, proposer_offer_json, receiver_offer_json, status, accepted_at, expires_at, created_at, updated_at
|
|
1313
|
+
FROM ${TABLES.RPG_TRADE_OFFER}
|
|
1314
|
+
WHERE id = ?
|
|
1315
|
+
LIMIT 1`,
|
|
1316
|
+
[offerId],
|
|
1317
|
+
connection,
|
|
1318
|
+
);
|
|
1319
|
+
return normalizeTradeOfferRow(rows?.[0] || null);
|
|
1320
|
+
};
|
|
1321
|
+
|
|
1322
|
+
export const getTradeOfferByIdForUpdate = async (offerId, connection) => {
|
|
1323
|
+
const rows = await executeQuery(
|
|
1324
|
+
`SELECT id, chat_jid, proposer_jid, receiver_jid, proposer_offer_json, receiver_offer_json, status, accepted_at, expires_at, created_at, updated_at
|
|
1325
|
+
FROM ${TABLES.RPG_TRADE_OFFER}
|
|
1326
|
+
WHERE id = ?
|
|
1327
|
+
LIMIT 1
|
|
1328
|
+
FOR UPDATE`,
|
|
1329
|
+
[offerId],
|
|
1330
|
+
connection,
|
|
1331
|
+
);
|
|
1332
|
+
return normalizeTradeOfferRow(rows?.[0] || null);
|
|
1333
|
+
};
|
|
1334
|
+
|
|
1335
|
+
export const listOpenTradeOffersByUser = async (ownerJid, connection = null) => {
|
|
1336
|
+
const rows = await executeQuery(
|
|
1337
|
+
`SELECT id, chat_jid, proposer_jid, receiver_jid, proposer_offer_json, receiver_offer_json, status, accepted_at, expires_at, created_at, updated_at
|
|
1338
|
+
FROM ${TABLES.RPG_TRADE_OFFER}
|
|
1339
|
+
WHERE status = 'pending'
|
|
1340
|
+
AND expires_at > UTC_TIMESTAMP()
|
|
1341
|
+
AND (proposer_jid = ? OR receiver_jid = ?)
|
|
1342
|
+
ORDER BY id DESC
|
|
1343
|
+
LIMIT 30`,
|
|
1344
|
+
[ownerJid, ownerJid],
|
|
1345
|
+
connection,
|
|
1346
|
+
);
|
|
1347
|
+
return (rows || []).map(normalizeTradeOfferRow);
|
|
1348
|
+
};
|
|
1349
|
+
|
|
1350
|
+
export const updateTradeOfferState = async ({ id, status, acceptedAt = undefined }, connection = null) => {
|
|
1351
|
+
const fields = ['status = ?', 'updated_at = CURRENT_TIMESTAMP'];
|
|
1352
|
+
const params = [status];
|
|
1353
|
+
if (acceptedAt !== undefined) {
|
|
1354
|
+
fields.push('accepted_at = ?');
|
|
1355
|
+
params.push(acceptedAt);
|
|
1356
|
+
}
|
|
1357
|
+
params.push(id);
|
|
1358
|
+
await executeQuery(
|
|
1359
|
+
`UPDATE ${TABLES.RPG_TRADE_OFFER}
|
|
1360
|
+
SET ${fields.join(', ')}
|
|
1361
|
+
WHERE id = ?`,
|
|
1362
|
+
params,
|
|
1363
|
+
connection,
|
|
1364
|
+
);
|
|
1365
|
+
};
|
|
1366
|
+
|
|
1367
|
+
export const expireOldTradeOffers = async (connection = null) => {
|
|
1368
|
+
const result = await executeQuery(
|
|
1369
|
+
`UPDATE ${TABLES.RPG_TRADE_OFFER}
|
|
1370
|
+
SET status = 'expired',
|
|
1371
|
+
updated_at = CURRENT_TIMESTAMP
|
|
1372
|
+
WHERE status = 'pending'
|
|
1373
|
+
AND expires_at <= UTC_TIMESTAMP()`,
|
|
1374
|
+
[],
|
|
1375
|
+
connection,
|
|
1376
|
+
);
|
|
1377
|
+
return Number(result?.affectedRows || 0);
|
|
1378
|
+
};
|
|
1379
|
+
|
|
1380
|
+
export const countPlayerPokemons = async (ownerJid, connection = null) => {
|
|
1381
|
+
const rows = await executeQuery(
|
|
1382
|
+
`SELECT COUNT(*) AS total
|
|
1383
|
+
FROM ${TABLES.RPG_PLAYER_POKEMON}
|
|
1384
|
+
WHERE owner_jid = ?`,
|
|
1385
|
+
[ownerJid],
|
|
1386
|
+
connection,
|
|
1387
|
+
);
|
|
1388
|
+
return Number(rows?.[0]?.total || 0);
|
|
1389
|
+
};
|
|
1390
|
+
|
|
1391
|
+
export const transferPlayerPokemon = async ({ pokemonId, fromOwnerJid, toOwnerJid }, connection = null) => {
|
|
1392
|
+
const result = await executeQuery(
|
|
1393
|
+
`UPDATE ${TABLES.RPG_PLAYER_POKEMON}
|
|
1394
|
+
SET owner_jid = ?,
|
|
1395
|
+
is_active = 0
|
|
1396
|
+
WHERE id = ?
|
|
1397
|
+
AND owner_jid = ?`,
|
|
1398
|
+
[toOwnerJid, pokemonId, fromOwnerJid],
|
|
1399
|
+
connection,
|
|
1400
|
+
);
|
|
1401
|
+
return Number(result?.affectedRows || 0) > 0;
|
|
1402
|
+
};
|
|
1403
|
+
|
|
1404
|
+
export const getFirstPlayerPokemon = async (ownerJid, connection = null) => {
|
|
1405
|
+
const rows = await executeQuery(
|
|
1406
|
+
`SELECT ${PLAYER_POKEMON_COLUMNS}
|
|
1407
|
+
FROM ${TABLES.RPG_PLAYER_POKEMON}
|
|
1408
|
+
WHERE owner_jid = ?
|
|
1409
|
+
ORDER BY id ASC
|
|
1410
|
+
LIMIT 1`,
|
|
1411
|
+
[ownerJid],
|
|
1412
|
+
connection,
|
|
1413
|
+
);
|
|
1414
|
+
return normalizePlayerPokemon(rows?.[0] || null);
|
|
1415
|
+
};
|
|
1416
|
+
|
|
1417
|
+
export const getGroupCoopWeekly = async (chatJid, weekRefDate, connection = null) => {
|
|
1418
|
+
const rows = await executeQuery(
|
|
1419
|
+
`SELECT chat_jid, week_ref_date, capture_target, raid_target, capture_progress, raid_progress, status, completed_at, created_at, updated_at
|
|
1420
|
+
FROM ${TABLES.RPG_GROUP_COOP_WEEKLY}
|
|
1421
|
+
WHERE chat_jid = ?
|
|
1422
|
+
AND week_ref_date = ?
|
|
1423
|
+
LIMIT 1`,
|
|
1424
|
+
[chatJid, weekRefDate],
|
|
1425
|
+
connection,
|
|
1426
|
+
);
|
|
1427
|
+
return normalizeCoopWeeklyRow(rows?.[0] || null);
|
|
1428
|
+
};
|
|
1429
|
+
|
|
1430
|
+
export const getGroupCoopWeeklyForUpdate = async (chatJid, weekRefDate, connection) => {
|
|
1431
|
+
const rows = await executeQuery(
|
|
1432
|
+
`SELECT chat_jid, week_ref_date, capture_target, raid_target, capture_progress, raid_progress, status, completed_at, created_at, updated_at
|
|
1433
|
+
FROM ${TABLES.RPG_GROUP_COOP_WEEKLY}
|
|
1434
|
+
WHERE chat_jid = ?
|
|
1435
|
+
AND week_ref_date = ?
|
|
1436
|
+
LIMIT 1
|
|
1437
|
+
FOR UPDATE`,
|
|
1438
|
+
[chatJid, weekRefDate],
|
|
1439
|
+
connection,
|
|
1440
|
+
);
|
|
1441
|
+
return normalizeCoopWeeklyRow(rows?.[0] || null);
|
|
1442
|
+
};
|
|
1443
|
+
|
|
1444
|
+
export const upsertGroupCoopWeekly = async ({ chatJid, weekRefDate, captureTarget = 20, raidTarget = 3 }, connection = null) => {
|
|
1445
|
+
await executeQuery(
|
|
1446
|
+
`INSERT INTO ${TABLES.RPG_GROUP_COOP_WEEKLY}
|
|
1447
|
+
(chat_jid, week_ref_date, capture_target, raid_target, capture_progress, raid_progress, status, completed_at)
|
|
1448
|
+
VALUES (?, ?, ?, ?, 0, 0, 'active', NULL)
|
|
1449
|
+
ON DUPLICATE KEY UPDATE
|
|
1450
|
+
capture_target = capture_target,
|
|
1451
|
+
raid_target = raid_target,
|
|
1452
|
+
updated_at = CURRENT_TIMESTAMP`,
|
|
1453
|
+
[chatJid, weekRefDate, captureTarget, raidTarget],
|
|
1454
|
+
connection,
|
|
1455
|
+
);
|
|
1456
|
+
};
|
|
1457
|
+
|
|
1458
|
+
export const addGroupCoopContribution = async ({ chatJid, weekRefDate, ownerJid, captureDelta = 0, raidDelta = 0 }, connection = null) => {
|
|
1459
|
+
await executeQuery(
|
|
1460
|
+
`UPDATE ${TABLES.RPG_GROUP_COOP_WEEKLY}
|
|
1461
|
+
SET capture_progress = capture_progress + ?,
|
|
1462
|
+
raid_progress = raid_progress + ?,
|
|
1463
|
+
updated_at = CURRENT_TIMESTAMP
|
|
1464
|
+
WHERE chat_jid = ?
|
|
1465
|
+
AND week_ref_date = ?`,
|
|
1466
|
+
[captureDelta, raidDelta, chatJid, weekRefDate],
|
|
1467
|
+
connection,
|
|
1468
|
+
);
|
|
1469
|
+
|
|
1470
|
+
await executeQuery(
|
|
1471
|
+
`INSERT INTO ${TABLES.RPG_GROUP_COOP_MEMBER}
|
|
1472
|
+
(chat_jid, week_ref_date, owner_jid, capture_contribution, raid_contribution, reward_claimed_at, last_contribution_at)
|
|
1473
|
+
VALUES (?, ?, ?, ?, ?, NULL, UTC_TIMESTAMP())
|
|
1474
|
+
ON DUPLICATE KEY UPDATE
|
|
1475
|
+
capture_contribution = capture_contribution + VALUES(capture_contribution),
|
|
1476
|
+
raid_contribution = raid_contribution + VALUES(raid_contribution),
|
|
1477
|
+
last_contribution_at = UTC_TIMESTAMP(),
|
|
1478
|
+
updated_at = CURRENT_TIMESTAMP`,
|
|
1479
|
+
[chatJid, weekRefDate, ownerJid, captureDelta, raidDelta],
|
|
1480
|
+
connection,
|
|
1481
|
+
);
|
|
1482
|
+
};
|
|
1483
|
+
|
|
1484
|
+
export const listGroupCoopMembers = async (chatJid, weekRefDate, connection = null) => {
|
|
1485
|
+
const rows = await executeQuery(
|
|
1486
|
+
`SELECT chat_jid, week_ref_date, owner_jid, capture_contribution, raid_contribution, reward_claimed_at, last_contribution_at, created_at, updated_at
|
|
1487
|
+
FROM ${TABLES.RPG_GROUP_COOP_MEMBER}
|
|
1488
|
+
WHERE chat_jid = ?
|
|
1489
|
+
AND week_ref_date = ?
|
|
1490
|
+
ORDER BY (capture_contribution + raid_contribution) DESC, updated_at DESC`,
|
|
1491
|
+
[chatJid, weekRefDate],
|
|
1492
|
+
connection,
|
|
1493
|
+
);
|
|
1494
|
+
return (rows || []).map(normalizeCoopMemberRow);
|
|
1495
|
+
};
|
|
1496
|
+
|
|
1497
|
+
export const getGroupCoopMember = async (chatJid, weekRefDate, ownerJid, connection = null) => {
|
|
1498
|
+
const rows = await executeQuery(
|
|
1499
|
+
`SELECT chat_jid, week_ref_date, owner_jid, capture_contribution, raid_contribution, reward_claimed_at, last_contribution_at, created_at, updated_at
|
|
1500
|
+
FROM ${TABLES.RPG_GROUP_COOP_MEMBER}
|
|
1501
|
+
WHERE chat_jid = ?
|
|
1502
|
+
AND week_ref_date = ?
|
|
1503
|
+
AND owner_jid = ?
|
|
1504
|
+
LIMIT 1`,
|
|
1505
|
+
[chatJid, weekRefDate, ownerJid],
|
|
1506
|
+
connection,
|
|
1507
|
+
);
|
|
1508
|
+
return normalizeCoopMemberRow(rows?.[0] || null);
|
|
1509
|
+
};
|
|
1510
|
+
|
|
1511
|
+
export const listUnrewardedGroupCoopMembersForUpdate = async (chatJid, weekRefDate, connection) => {
|
|
1512
|
+
const rows = await executeQuery(
|
|
1513
|
+
`SELECT chat_jid, week_ref_date, owner_jid, capture_contribution, raid_contribution, reward_claimed_at, last_contribution_at, created_at, updated_at
|
|
1514
|
+
FROM ${TABLES.RPG_GROUP_COOP_MEMBER}
|
|
1515
|
+
WHERE chat_jid = ?
|
|
1516
|
+
AND week_ref_date = ?
|
|
1517
|
+
AND reward_claimed_at IS NULL
|
|
1518
|
+
FOR UPDATE`,
|
|
1519
|
+
[chatJid, weekRefDate],
|
|
1520
|
+
connection,
|
|
1521
|
+
);
|
|
1522
|
+
return (rows || []).map(normalizeCoopMemberRow);
|
|
1523
|
+
};
|
|
1524
|
+
|
|
1525
|
+
export const markGroupCoopCompleted = async (chatJid, weekRefDate, connection = null) => {
|
|
1526
|
+
await executeQuery(
|
|
1527
|
+
`UPDATE ${TABLES.RPG_GROUP_COOP_WEEKLY}
|
|
1528
|
+
SET status = 'completed',
|
|
1529
|
+
completed_at = COALESCE(completed_at, UTC_TIMESTAMP()),
|
|
1530
|
+
updated_at = CURRENT_TIMESTAMP
|
|
1531
|
+
WHERE chat_jid = ?
|
|
1532
|
+
AND week_ref_date = ?`,
|
|
1533
|
+
[chatJid, weekRefDate],
|
|
1534
|
+
connection,
|
|
1535
|
+
);
|
|
1536
|
+
};
|
|
1537
|
+
|
|
1538
|
+
export const markGroupCoopMemberRewardClaimed = async (chatJid, weekRefDate, ownerJid, connection = null) => {
|
|
1539
|
+
await executeQuery(
|
|
1540
|
+
`UPDATE ${TABLES.RPG_GROUP_COOP_MEMBER}
|
|
1541
|
+
SET reward_claimed_at = COALESCE(reward_claimed_at, UTC_TIMESTAMP()),
|
|
1542
|
+
updated_at = CURRENT_TIMESTAMP
|
|
1543
|
+
WHERE chat_jid = ?
|
|
1544
|
+
AND week_ref_date = ?
|
|
1545
|
+
AND owner_jid = ?`,
|
|
1546
|
+
[chatJid, weekRefDate, ownerJid],
|
|
1547
|
+
connection,
|
|
1548
|
+
);
|
|
1549
|
+
};
|
|
1550
|
+
|
|
1551
|
+
export const getGroupEventWeekly = async (chatJid, weekRefDate, connection = null) => {
|
|
1552
|
+
const rows = await executeQuery(
|
|
1553
|
+
`SELECT chat_jid, week_ref_date, event_key, target_value, progress_value, status, expires_at, completed_at, created_at, updated_at
|
|
1554
|
+
FROM ${TABLES.RPG_GROUP_EVENT_WEEKLY}
|
|
1555
|
+
WHERE chat_jid = ?
|
|
1556
|
+
AND week_ref_date = ?
|
|
1557
|
+
LIMIT 1`,
|
|
1558
|
+
[chatJid, weekRefDate],
|
|
1559
|
+
connection,
|
|
1560
|
+
);
|
|
1561
|
+
return normalizeGroupEventRow(rows?.[0] || null);
|
|
1562
|
+
};
|
|
1563
|
+
|
|
1564
|
+
export const getGroupEventWeeklyForUpdate = async (chatJid, weekRefDate, connection) => {
|
|
1565
|
+
const rows = await executeQuery(
|
|
1566
|
+
`SELECT chat_jid, week_ref_date, event_key, target_value, progress_value, status, expires_at, completed_at, created_at, updated_at
|
|
1567
|
+
FROM ${TABLES.RPG_GROUP_EVENT_WEEKLY}
|
|
1568
|
+
WHERE chat_jid = ?
|
|
1569
|
+
AND week_ref_date = ?
|
|
1570
|
+
LIMIT 1
|
|
1571
|
+
FOR UPDATE`,
|
|
1572
|
+
[chatJid, weekRefDate],
|
|
1573
|
+
connection,
|
|
1574
|
+
);
|
|
1575
|
+
return normalizeGroupEventRow(rows?.[0] || null);
|
|
1576
|
+
};
|
|
1577
|
+
|
|
1578
|
+
export const upsertGroupEventWeekly = async ({ chatJid, weekRefDate, eventKey, targetValue, expiresAt }, connection = null) => {
|
|
1579
|
+
await executeQuery(
|
|
1580
|
+
`INSERT INTO ${TABLES.RPG_GROUP_EVENT_WEEKLY}
|
|
1581
|
+
(chat_jid, week_ref_date, event_key, target_value, progress_value, status, expires_at, completed_at)
|
|
1582
|
+
VALUES (?, ?, ?, ?, 0, 'active', ?, NULL)
|
|
1583
|
+
ON DUPLICATE KEY UPDATE
|
|
1584
|
+
event_key = event_key,
|
|
1585
|
+
target_value = target_value,
|
|
1586
|
+
expires_at = expires_at,
|
|
1587
|
+
updated_at = CURRENT_TIMESTAMP`,
|
|
1588
|
+
[chatJid, weekRefDate, eventKey, targetValue, expiresAt],
|
|
1589
|
+
connection,
|
|
1590
|
+
);
|
|
1591
|
+
};
|
|
1592
|
+
|
|
1593
|
+
export const addGroupEventContribution = async ({ chatJid, weekRefDate, ownerJid, contributionDelta = 0 }, connection = null) => {
|
|
1594
|
+
await executeQuery(
|
|
1595
|
+
`UPDATE ${TABLES.RPG_GROUP_EVENT_WEEKLY}
|
|
1596
|
+
SET progress_value = progress_value + ?,
|
|
1597
|
+
updated_at = CURRENT_TIMESTAMP
|
|
1598
|
+
WHERE chat_jid = ?
|
|
1599
|
+
AND week_ref_date = ?`,
|
|
1600
|
+
[contributionDelta, chatJid, weekRefDate],
|
|
1601
|
+
connection,
|
|
1602
|
+
);
|
|
1603
|
+
|
|
1604
|
+
await executeQuery(
|
|
1605
|
+
`INSERT INTO ${TABLES.RPG_GROUP_EVENT_MEMBER}
|
|
1606
|
+
(chat_jid, week_ref_date, owner_jid, contribution, reward_claimed_at, last_contribution_at)
|
|
1607
|
+
VALUES (?, ?, ?, ?, NULL, UTC_TIMESTAMP())
|
|
1608
|
+
ON DUPLICATE KEY UPDATE
|
|
1609
|
+
contribution = contribution + VALUES(contribution),
|
|
1610
|
+
last_contribution_at = UTC_TIMESTAMP(),
|
|
1611
|
+
updated_at = CURRENT_TIMESTAMP`,
|
|
1612
|
+
[chatJid, weekRefDate, ownerJid, contributionDelta],
|
|
1613
|
+
connection,
|
|
1614
|
+
);
|
|
1615
|
+
};
|
|
1616
|
+
|
|
1617
|
+
export const listGroupEventMembers = async (chatJid, weekRefDate, limit = 10, connection = null) => {
|
|
1618
|
+
const safeLimit = Math.max(1, Math.min(50, Number(limit) || 10));
|
|
1619
|
+
const rows = await executeQuery(
|
|
1620
|
+
`SELECT chat_jid, week_ref_date, owner_jid, contribution, reward_claimed_at, last_contribution_at, created_at, updated_at
|
|
1621
|
+
FROM ${TABLES.RPG_GROUP_EVENT_MEMBER}
|
|
1622
|
+
WHERE chat_jid = ?
|
|
1623
|
+
AND week_ref_date = ?
|
|
1624
|
+
ORDER BY contribution DESC, updated_at DESC
|
|
1625
|
+
LIMIT ${safeLimit}`,
|
|
1626
|
+
[chatJid, weekRefDate],
|
|
1627
|
+
connection,
|
|
1628
|
+
);
|
|
1629
|
+
return (rows || []).map(normalizeGroupEventMemberRow);
|
|
1630
|
+
};
|
|
1631
|
+
|
|
1632
|
+
export const getGroupEventMember = async (chatJid, weekRefDate, ownerJid, connection = null) => {
|
|
1633
|
+
const rows = await executeQuery(
|
|
1634
|
+
`SELECT chat_jid, week_ref_date, owner_jid, contribution, reward_claimed_at, last_contribution_at, created_at, updated_at
|
|
1635
|
+
FROM ${TABLES.RPG_GROUP_EVENT_MEMBER}
|
|
1636
|
+
WHERE chat_jid = ?
|
|
1637
|
+
AND week_ref_date = ?
|
|
1638
|
+
AND owner_jid = ?
|
|
1639
|
+
LIMIT 1`,
|
|
1640
|
+
[chatJid, weekRefDate, ownerJid],
|
|
1641
|
+
connection,
|
|
1642
|
+
);
|
|
1643
|
+
return normalizeGroupEventMemberRow(rows?.[0] || null);
|
|
1644
|
+
};
|
|
1645
|
+
|
|
1646
|
+
export const getGroupEventMemberForUpdate = async (chatJid, weekRefDate, ownerJid, connection) => {
|
|
1647
|
+
const rows = await executeQuery(
|
|
1648
|
+
`SELECT chat_jid, week_ref_date, owner_jid, contribution, reward_claimed_at, last_contribution_at, created_at, updated_at
|
|
1649
|
+
FROM ${TABLES.RPG_GROUP_EVENT_MEMBER}
|
|
1650
|
+
WHERE chat_jid = ?
|
|
1651
|
+
AND week_ref_date = ?
|
|
1652
|
+
AND owner_jid = ?
|
|
1653
|
+
LIMIT 1
|
|
1654
|
+
FOR UPDATE`,
|
|
1655
|
+
[chatJid, weekRefDate, ownerJid],
|
|
1656
|
+
connection,
|
|
1657
|
+
);
|
|
1658
|
+
return normalizeGroupEventMemberRow(rows?.[0] || null);
|
|
1659
|
+
};
|
|
1660
|
+
|
|
1661
|
+
export const markGroupEventCompleted = async (chatJid, weekRefDate, connection = null) => {
|
|
1662
|
+
await executeQuery(
|
|
1663
|
+
`UPDATE ${TABLES.RPG_GROUP_EVENT_WEEKLY}
|
|
1664
|
+
SET status = 'completed',
|
|
1665
|
+
completed_at = COALESCE(completed_at, UTC_TIMESTAMP()),
|
|
1666
|
+
updated_at = CURRENT_TIMESTAMP
|
|
1667
|
+
WHERE chat_jid = ?
|
|
1668
|
+
AND week_ref_date = ?`,
|
|
1669
|
+
[chatJid, weekRefDate],
|
|
1670
|
+
connection,
|
|
1671
|
+
);
|
|
1672
|
+
};
|
|
1673
|
+
|
|
1674
|
+
export const markGroupEventMemberRewardClaimed = async (chatJid, weekRefDate, ownerJid, connection = null) => {
|
|
1675
|
+
await executeQuery(
|
|
1676
|
+
`UPDATE ${TABLES.RPG_GROUP_EVENT_MEMBER}
|
|
1677
|
+
SET reward_claimed_at = COALESCE(reward_claimed_at, UTC_TIMESTAMP()),
|
|
1678
|
+
updated_at = CURRENT_TIMESTAMP
|
|
1679
|
+
WHERE chat_jid = ?
|
|
1680
|
+
AND week_ref_date = ?
|
|
1681
|
+
AND owner_jid = ?`,
|
|
1682
|
+
[chatJid, weekRefDate, ownerJid],
|
|
1683
|
+
connection,
|
|
1684
|
+
);
|
|
1685
|
+
};
|
|
1686
|
+
|
|
1687
|
+
export const getKarmaProfile = async (ownerJid, connection = null) => {
|
|
1688
|
+
const rows = await executeQuery(
|
|
1689
|
+
`SELECT owner_jid, karma_score, positive_votes, negative_votes, created_at, updated_at
|
|
1690
|
+
FROM ${TABLES.RPG_KARMA_PROFILE}
|
|
1691
|
+
WHERE owner_jid = ?
|
|
1692
|
+
LIMIT 1`,
|
|
1693
|
+
[ownerJid],
|
|
1694
|
+
connection,
|
|
1695
|
+
);
|
|
1696
|
+
return normalizeKarmaProfileRow(rows?.[0] || null);
|
|
1697
|
+
};
|
|
1698
|
+
|
|
1699
|
+
export const getKarmaVoteByWeekForUpdate = async (weekRefDate, voterJid, targetJid, connection) => {
|
|
1700
|
+
const rows = await executeQuery(
|
|
1701
|
+
`SELECT id, week_ref_date, voter_jid, target_jid, vote_value, created_at
|
|
1702
|
+
FROM ${TABLES.RPG_KARMA_VOTE_HISTORY}
|
|
1703
|
+
WHERE week_ref_date = ?
|
|
1704
|
+
AND voter_jid = ?
|
|
1705
|
+
AND target_jid = ?
|
|
1706
|
+
LIMIT 1
|
|
1707
|
+
FOR UPDATE`,
|
|
1708
|
+
[weekRefDate, voterJid, targetJid],
|
|
1709
|
+
connection,
|
|
1710
|
+
);
|
|
1711
|
+
return rows?.[0] || null;
|
|
1712
|
+
};
|
|
1713
|
+
|
|
1714
|
+
export const createKarmaVote = async ({ weekRefDate, voterJid, targetJid, voteValue }, connection = null) => {
|
|
1715
|
+
const result = await executeQuery(
|
|
1716
|
+
`INSERT INTO ${TABLES.RPG_KARMA_VOTE_HISTORY}
|
|
1717
|
+
(week_ref_date, voter_jid, target_jid, vote_value)
|
|
1718
|
+
VALUES (?, ?, ?, ?)`,
|
|
1719
|
+
[weekRefDate, voterJid, targetJid, voteValue],
|
|
1720
|
+
connection,
|
|
1721
|
+
);
|
|
1722
|
+
return Number(result?.insertId || 0) > 0;
|
|
1723
|
+
};
|
|
1724
|
+
|
|
1725
|
+
export const applyKarmaDelta = async ({ ownerJid, karmaDelta = 0, positiveDelta = 0, negativeDelta = 0 }, connection = null) => {
|
|
1726
|
+
await executeQuery(
|
|
1727
|
+
`INSERT INTO ${TABLES.RPG_KARMA_PROFILE}
|
|
1728
|
+
(owner_jid, karma_score, positive_votes, negative_votes)
|
|
1729
|
+
VALUES (?, ?, ?, ?)
|
|
1730
|
+
ON DUPLICATE KEY UPDATE
|
|
1731
|
+
karma_score = karma_score + VALUES(karma_score),
|
|
1732
|
+
positive_votes = positive_votes + VALUES(positive_votes),
|
|
1733
|
+
negative_votes = negative_votes + VALUES(negative_votes),
|
|
1734
|
+
updated_at = CURRENT_TIMESTAMP`,
|
|
1735
|
+
[ownerJid, karmaDelta, positiveDelta, negativeDelta],
|
|
1736
|
+
connection,
|
|
1737
|
+
);
|
|
1738
|
+
};
|
|
1739
|
+
|
|
1740
|
+
export const listTopKarmaProfiles = async (limit = 10, connection = null) => {
|
|
1741
|
+
const safeLimit = Math.max(1, Math.min(30, Number(limit) || 10));
|
|
1742
|
+
const rows = await executeQuery(
|
|
1743
|
+
`SELECT owner_jid, karma_score, positive_votes, negative_votes, created_at, updated_at
|
|
1744
|
+
FROM ${TABLES.RPG_KARMA_PROFILE}
|
|
1745
|
+
ORDER BY karma_score DESC, positive_votes DESC, updated_at DESC
|
|
1746
|
+
LIMIT ${safeLimit}`,
|
|
1747
|
+
[],
|
|
1748
|
+
connection,
|
|
1749
|
+
);
|
|
1750
|
+
return (rows || []).map(normalizeKarmaProfileRow);
|
|
1751
|
+
};
|
|
1752
|
+
|
|
1753
|
+
export const upsertGroupActivityDaily = async ({ dayRefDate, chatJid, ownerJid, actionsDelta = 0, pvpCreatedDelta = 0, pvpCompletedDelta = 0, coopCompletedDelta = 0 }, connection = null) => {
|
|
1754
|
+
await executeQuery(
|
|
1755
|
+
`INSERT INTO ${TABLES.RPG_GROUP_ACTIVITY_DAILY}
|
|
1756
|
+
(day_ref_date, chat_jid, owner_jid, actions_count, pvp_created_count, pvp_completed_count, coop_completed_count)
|
|
1757
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1758
|
+
ON DUPLICATE KEY UPDATE
|
|
1759
|
+
actions_count = actions_count + VALUES(actions_count),
|
|
1760
|
+
pvp_created_count = pvp_created_count + VALUES(pvp_created_count),
|
|
1761
|
+
pvp_completed_count = pvp_completed_count + VALUES(pvp_completed_count),
|
|
1762
|
+
coop_completed_count = coop_completed_count + VALUES(coop_completed_count),
|
|
1763
|
+
updated_at = CURRENT_TIMESTAMP`,
|
|
1764
|
+
[dayRefDate, chatJid, ownerJid, actionsDelta, pvpCreatedDelta, pvpCompletedDelta, coopCompletedDelta],
|
|
1765
|
+
connection,
|
|
1766
|
+
);
|
|
1767
|
+
};
|
|
1768
|
+
|
|
1769
|
+
export const getSocialXpDailyByKeyForUpdate = async (dayRefDate, ownerJid, chatJid, connection) => {
|
|
1770
|
+
await executeQuery(
|
|
1771
|
+
`INSERT INTO ${TABLES.RPG_SOCIAL_XP_DAILY}
|
|
1772
|
+
(day_ref_date, owner_jid, chat_jid, earned_xp, converted_xp, cap_hits, last_message_hash, last_earned_at)
|
|
1773
|
+
VALUES (?, ?, ?, 0, 0, 0, NULL, NULL)
|
|
1774
|
+
ON DUPLICATE KEY UPDATE updated_at = updated_at`,
|
|
1775
|
+
[dayRefDate, ownerJid, chatJid],
|
|
1776
|
+
connection,
|
|
1777
|
+
);
|
|
1778
|
+
|
|
1779
|
+
const rows = await executeQuery(
|
|
1780
|
+
`SELECT ${SOCIAL_XP_DAILY_COLUMNS}
|
|
1781
|
+
FROM ${TABLES.RPG_SOCIAL_XP_DAILY}
|
|
1782
|
+
WHERE day_ref_date = ?
|
|
1783
|
+
AND owner_jid = ?
|
|
1784
|
+
AND chat_jid = ?
|
|
1785
|
+
LIMIT 1
|
|
1786
|
+
FOR UPDATE`,
|
|
1787
|
+
[dayRefDate, ownerJid, chatJid],
|
|
1788
|
+
connection,
|
|
1789
|
+
);
|
|
1790
|
+
|
|
1791
|
+
return normalizeSocialXpDailyRow(rows?.[0] || null);
|
|
1792
|
+
};
|
|
1793
|
+
|
|
1794
|
+
export const upsertSocialXpDailyDelta = async ({ dayRefDate, ownerJid, chatJid, earnedDelta = 0, convertedDelta = 0, capHitsDelta = 0, lastMessageHash = null, lastEarnedAt = null }, connection = null) => {
|
|
1795
|
+
await executeQuery(
|
|
1796
|
+
`INSERT INTO ${TABLES.RPG_SOCIAL_XP_DAILY}
|
|
1797
|
+
(day_ref_date, owner_jid, chat_jid, earned_xp, converted_xp, cap_hits, last_message_hash, last_earned_at)
|
|
1798
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
1799
|
+
ON DUPLICATE KEY UPDATE
|
|
1800
|
+
earned_xp = GREATEST(0, earned_xp + VALUES(earned_xp)),
|
|
1801
|
+
converted_xp = GREATEST(0, converted_xp + VALUES(converted_xp)),
|
|
1802
|
+
cap_hits = GREATEST(0, cap_hits + VALUES(cap_hits)),
|
|
1803
|
+
last_message_hash = COALESCE(VALUES(last_message_hash), last_message_hash),
|
|
1804
|
+
last_earned_at = COALESCE(VALUES(last_earned_at), last_earned_at),
|
|
1805
|
+
updated_at = CURRENT_TIMESTAMP`,
|
|
1806
|
+
[dayRefDate, ownerJid, chatJid, Math.max(0, Number(earnedDelta) || 0), Math.max(0, Number(convertedDelta) || 0), Math.max(0, Number(capHitsDelta) || 0), lastMessageHash || null, lastEarnedAt || null],
|
|
1807
|
+
connection,
|
|
1808
|
+
);
|
|
1809
|
+
};
|
|
1810
|
+
|
|
1811
|
+
export const getGroupActiveUsersByDay = async (chatJid, dayRefDate, connection = null) => {
|
|
1812
|
+
const rows = await executeQuery(
|
|
1813
|
+
`SELECT COUNT(*) AS total
|
|
1814
|
+
FROM ${TABLES.RPG_GROUP_ACTIVITY_DAILY}
|
|
1815
|
+
WHERE chat_jid = ?
|
|
1816
|
+
AND day_ref_date = ?`,
|
|
1817
|
+
[chatJid, dayRefDate],
|
|
1818
|
+
connection,
|
|
1819
|
+
);
|
|
1820
|
+
return Number(rows?.[0]?.total || 0);
|
|
1821
|
+
};
|
|
1822
|
+
|
|
1823
|
+
export const getGroupRetentionByDays = async (chatJid, currentDayRefDate, previousDayRefDate, connection = null) => {
|
|
1824
|
+
const rows = await executeQuery(
|
|
1825
|
+
`SELECT COUNT(*) AS retained
|
|
1826
|
+
FROM ${TABLES.RPG_GROUP_ACTIVITY_DAILY} cur
|
|
1827
|
+
INNER JOIN ${TABLES.RPG_GROUP_ACTIVITY_DAILY} prev
|
|
1828
|
+
ON prev.chat_jid = cur.chat_jid
|
|
1829
|
+
AND prev.owner_jid = cur.owner_jid
|
|
1830
|
+
AND prev.day_ref_date = ?
|
|
1831
|
+
WHERE cur.chat_jid = ?
|
|
1832
|
+
AND cur.day_ref_date = ?`,
|
|
1833
|
+
[previousDayRefDate, chatJid, currentDayRefDate],
|
|
1834
|
+
connection,
|
|
1835
|
+
);
|
|
1836
|
+
return Number(rows?.[0]?.retained || 0);
|
|
1837
|
+
};
|
|
1838
|
+
|
|
1839
|
+
export const getGroupActivitySummaryByDay = async (chatJid, dayRefDate, connection = null) => {
|
|
1840
|
+
const rows = await executeQuery(
|
|
1841
|
+
`SELECT
|
|
1842
|
+
COALESCE(SUM(actions_count), 0) AS actions_total,
|
|
1843
|
+
COALESCE(SUM(pvp_created_count), 0) AS pvp_created_total,
|
|
1844
|
+
COALESCE(SUM(pvp_completed_count), 0) AS pvp_completed_total,
|
|
1845
|
+
COALESCE(SUM(coop_completed_count), 0) AS coop_completed_total
|
|
1846
|
+
FROM ${TABLES.RPG_GROUP_ACTIVITY_DAILY}
|
|
1847
|
+
WHERE chat_jid = ?
|
|
1848
|
+
AND day_ref_date = ?`,
|
|
1849
|
+
[chatJid, dayRefDate],
|
|
1850
|
+
connection,
|
|
1851
|
+
);
|
|
1852
|
+
const row = rows?.[0] || {};
|
|
1853
|
+
return {
|
|
1854
|
+
actionsTotal: Number(row.actions_total || 0),
|
|
1855
|
+
pvpCreatedTotal: Number(row.pvp_created_total || 0),
|
|
1856
|
+
pvpCompletedTotal: Number(row.pvp_completed_total || 0),
|
|
1857
|
+
coopCompletedTotal: Number(row.coop_completed_total || 0),
|
|
1858
|
+
};
|
|
1859
|
+
};
|