@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
package/database/init.js
ADDED
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
import mysql from 'mysql2/promise';
|
|
2
|
+
import logger from '../app/utils/logger/loggerModule.js';
|
|
3
|
+
import { dbConfig, TABLES } from './index.js';
|
|
4
|
+
import { promises as fs } from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Nome do banco de dados que será criado ou validado.
|
|
10
|
+
* Já vem resolvido com sufixo _dev ou _prod via dbConfig.
|
|
11
|
+
* @type {string}
|
|
12
|
+
*/
|
|
13
|
+
const dbToCreate = dbConfig.database;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* SQL para criar o banco de dados caso ainda não exista.
|
|
17
|
+
* Usa:
|
|
18
|
+
* - utf8mb4: suporte total a emojis e caracteres unicode
|
|
19
|
+
* - utf8mb4_unicode_ci: collation consistente para buscas
|
|
20
|
+
*/
|
|
21
|
+
const createDatabaseSQL = `
|
|
22
|
+
CREATE DATABASE IF NOT EXISTS \`${dbToCreate}\`
|
|
23
|
+
DEFAULT CHARACTER SET utf8mb4
|
|
24
|
+
DEFAULT COLLATE utf8mb4_unicode_ci;
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Tabela MESSAGES
|
|
29
|
+
* Armazena todas as mensagens processadas pelo sistema.
|
|
30
|
+
*
|
|
31
|
+
* Campos importantes:
|
|
32
|
+
* - message_id: ID global do WhatsApp (único)
|
|
33
|
+
* - chat_id: conversa (grupo ou privado)
|
|
34
|
+
* - sender_id: remetente da mensagem
|
|
35
|
+
* - content: texto extraído
|
|
36
|
+
* - raw_message: JSON original da Baileys
|
|
37
|
+
* - timestamp: timestamp da mensagem no WhatsApp
|
|
38
|
+
*
|
|
39
|
+
* Índices:
|
|
40
|
+
* - chat_id + timestamp → leitura de histórico por conversa
|
|
41
|
+
* - sender_id → estatísticas por usuário
|
|
42
|
+
* - timestamp → ordenação global
|
|
43
|
+
*/
|
|
44
|
+
const createMessagesTableSQL = `
|
|
45
|
+
CREATE TABLE IF NOT EXISTS ${TABLES.MESSAGES} (
|
|
46
|
+
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
|
|
47
|
+
message_id VARCHAR(255) UNIQUE NOT NULL,
|
|
48
|
+
chat_id VARCHAR(255) NOT NULL,
|
|
49
|
+
sender_id VARCHAR(255),
|
|
50
|
+
content TEXT,
|
|
51
|
+
raw_message JSON,
|
|
52
|
+
timestamp TIMESTAMP NULL,
|
|
53
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
54
|
+
INDEX idx_chat_timestamp (chat_id, timestamp),
|
|
55
|
+
INDEX idx_sender (sender_id),
|
|
56
|
+
INDEX idx_timestamp (timestamp)
|
|
57
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Tabela CHATS
|
|
62
|
+
* Representa uma conversa (grupo ou privado).
|
|
63
|
+
*
|
|
64
|
+
* - id → JID do WhatsApp
|
|
65
|
+
* - name → nome do grupo ou contato
|
|
66
|
+
* - raw_chat → JSON bruto retornado pela Baileys
|
|
67
|
+
*/
|
|
68
|
+
const createChatsTableSQL = `
|
|
69
|
+
CREATE TABLE IF NOT EXISTS ${TABLES.CHATS} (
|
|
70
|
+
id VARCHAR(255) PRIMARY KEY NOT NULL,
|
|
71
|
+
name VARCHAR(255),
|
|
72
|
+
raw_chat JSON,
|
|
73
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
|
74
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Tabela GROUPS_METADATA
|
|
79
|
+
* Armazena informações normalizadas de grupos.
|
|
80
|
+
*
|
|
81
|
+
* Usada para:
|
|
82
|
+
* - admin commands
|
|
83
|
+
* - estatísticas
|
|
84
|
+
* - sincronização de participantes
|
|
85
|
+
*
|
|
86
|
+
* participants é JSON para armazenar:
|
|
87
|
+
* [{ jid, lid, admin }, ...]
|
|
88
|
+
*/
|
|
89
|
+
const createGroupsMetadataTableSQL = `
|
|
90
|
+
CREATE TABLE IF NOT EXISTS ${TABLES.GROUPS_METADATA} (
|
|
91
|
+
id VARCHAR(255) PRIMARY KEY NOT NULL,
|
|
92
|
+
subject VARCHAR(255),
|
|
93
|
+
description TEXT,
|
|
94
|
+
owner_jid VARCHAR(255),
|
|
95
|
+
creation BIGINT,
|
|
96
|
+
participants JSON,
|
|
97
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
|
98
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
99
|
+
`;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Tabela GROUP_CONFIGS
|
|
103
|
+
* Configurações específicas por grupo.
|
|
104
|
+
*
|
|
105
|
+
* Exemplo:
|
|
106
|
+
* - prefixo de comandos
|
|
107
|
+
* - anti-link
|
|
108
|
+
* - modo restrito
|
|
109
|
+
*/
|
|
110
|
+
const createGroupConfigsTableSQL = `
|
|
111
|
+
CREATE TABLE IF NOT EXISTS ${TABLES.GROUP_CONFIGS} (
|
|
112
|
+
id VARCHAR(255) PRIMARY KEY NOT NULL,
|
|
113
|
+
config JSON,
|
|
114
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
|
115
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
116
|
+
`;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Tabela LID_MAP
|
|
120
|
+
* Mapeia IDs temporários do WhatsApp (LID) para JIDs reais.
|
|
121
|
+
*
|
|
122
|
+
* Isso resolve problemas de:
|
|
123
|
+
* - mensagens vindo com "xxx@lid"
|
|
124
|
+
* - alternância entre lid e s.whatsapp.net
|
|
125
|
+
*
|
|
126
|
+
* Campos:
|
|
127
|
+
* - lid → chave primária
|
|
128
|
+
* - jid → jid real associado
|
|
129
|
+
* - first_seen → quando apareceu
|
|
130
|
+
* - last_seen → última vez visto
|
|
131
|
+
* - source → origem do dado (message, group, etc)
|
|
132
|
+
*/
|
|
133
|
+
const createLidMapTableSQL = `
|
|
134
|
+
CREATE TABLE IF NOT EXISTS ${TABLES.LID_MAP} (
|
|
135
|
+
lid VARCHAR(64) PRIMARY KEY,
|
|
136
|
+
jid VARCHAR(64) NULL,
|
|
137
|
+
first_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
138
|
+
last_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
139
|
+
source VARCHAR(32),
|
|
140
|
+
INDEX idx_lid_map_jid (jid),
|
|
141
|
+
INDEX idx_lid_map_last_seen (last_seen)
|
|
142
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
143
|
+
`;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Tabela STICKER_ASSET
|
|
147
|
+
* Armazena os arquivos de figurinha persistidos no storage local.
|
|
148
|
+
*/
|
|
149
|
+
const createStickerAssetTableSQL = `
|
|
150
|
+
CREATE TABLE IF NOT EXISTS ${TABLES.STICKER_ASSET} (
|
|
151
|
+
id CHAR(36) PRIMARY KEY,
|
|
152
|
+
owner_jid VARCHAR(255) NOT NULL,
|
|
153
|
+
sha256 CHAR(64) NOT NULL,
|
|
154
|
+
mimetype VARCHAR(64) NOT NULL,
|
|
155
|
+
is_animated TINYINT(1) NOT NULL DEFAULT 0,
|
|
156
|
+
width INT UNSIGNED NULL,
|
|
157
|
+
height INT UNSIGNED NULL,
|
|
158
|
+
size_bytes INT UNSIGNED NOT NULL,
|
|
159
|
+
storage_path VARCHAR(1024) NOT NULL,
|
|
160
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
161
|
+
UNIQUE KEY uq_sticker_asset_sha256 (sha256),
|
|
162
|
+
INDEX idx_sticker_asset_owner_created (owner_jid, created_at)
|
|
163
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
164
|
+
`;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Tabela STICKER_PACK
|
|
168
|
+
* Metadados do pack (dono, nome, publisher, capa, visibilidade).
|
|
169
|
+
*/
|
|
170
|
+
const createStickerPackTableSQL = `
|
|
171
|
+
CREATE TABLE IF NOT EXISTS ${TABLES.STICKER_PACK} (
|
|
172
|
+
id CHAR(36) PRIMARY KEY,
|
|
173
|
+
owner_jid VARCHAR(255) NOT NULL,
|
|
174
|
+
name VARCHAR(120) NOT NULL,
|
|
175
|
+
publisher VARCHAR(120) NOT NULL,
|
|
176
|
+
description TEXT NULL,
|
|
177
|
+
pack_key VARCHAR(160) NOT NULL,
|
|
178
|
+
cover_sticker_id CHAR(36) NULL,
|
|
179
|
+
visibility ENUM('private', 'public', 'unlisted') NOT NULL DEFAULT 'private',
|
|
180
|
+
status ENUM('draft', 'uploading', 'processing', 'published', 'failed') NOT NULL DEFAULT 'published',
|
|
181
|
+
version INT UNSIGNED NOT NULL DEFAULT 1,
|
|
182
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
183
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
184
|
+
deleted_at TIMESTAMP NULL DEFAULT NULL,
|
|
185
|
+
UNIQUE KEY uq_sticker_pack_pack_key (pack_key),
|
|
186
|
+
INDEX idx_sticker_pack_owner_deleted (owner_jid, deleted_at),
|
|
187
|
+
INDEX idx_sticker_pack_owner_updated (owner_jid, updated_at),
|
|
188
|
+
CONSTRAINT fk_sticker_pack_cover
|
|
189
|
+
FOREIGN KEY (cover_sticker_id) REFERENCES ${TABLES.STICKER_ASSET}(id)
|
|
190
|
+
ON DELETE SET NULL ON UPDATE CASCADE
|
|
191
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
192
|
+
`;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Tabela STICKER_PACK_ITEM
|
|
196
|
+
* Relação N:N ordenada entre pack e figurinhas.
|
|
197
|
+
*/
|
|
198
|
+
const createStickerPackItemTableSQL = `
|
|
199
|
+
CREATE TABLE IF NOT EXISTS ${TABLES.STICKER_PACK_ITEM} (
|
|
200
|
+
id CHAR(36) PRIMARY KEY,
|
|
201
|
+
pack_id CHAR(36) NOT NULL,
|
|
202
|
+
sticker_id CHAR(36) NOT NULL,
|
|
203
|
+
position INT UNSIGNED NOT NULL,
|
|
204
|
+
emojis JSON NULL,
|
|
205
|
+
accessibility_label VARCHAR(255) NULL,
|
|
206
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
207
|
+
UNIQUE KEY uq_sticker_pack_item_pack_sticker (pack_id, sticker_id),
|
|
208
|
+
UNIQUE KEY uq_sticker_pack_item_pack_position (pack_id, position),
|
|
209
|
+
INDEX idx_sticker_pack_item_pack_position (pack_id, position),
|
|
210
|
+
CONSTRAINT fk_sticker_pack_item_pack
|
|
211
|
+
FOREIGN KEY (pack_id) REFERENCES ${TABLES.STICKER_PACK}(id)
|
|
212
|
+
ON DELETE CASCADE ON UPDATE CASCADE,
|
|
213
|
+
CONSTRAINT fk_sticker_pack_item_asset
|
|
214
|
+
FOREIGN KEY (sticker_id) REFERENCES ${TABLES.STICKER_ASSET}(id)
|
|
215
|
+
ON DELETE RESTRICT ON UPDATE CASCADE
|
|
216
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
217
|
+
`;
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Tabela STICKER_PACK_WEB_UPLOAD
|
|
221
|
+
* Controle de upload idempotente do fluxo web (upload_id + hash por pack).
|
|
222
|
+
*/
|
|
223
|
+
const createStickerPackWebUploadTableSQL = `
|
|
224
|
+
CREATE TABLE IF NOT EXISTS ${TABLES.STICKER_PACK_WEB_UPLOAD} (
|
|
225
|
+
id CHAR(36) PRIMARY KEY,
|
|
226
|
+
pack_id CHAR(36) NOT NULL,
|
|
227
|
+
upload_id VARCHAR(120) NOT NULL,
|
|
228
|
+
sticker_hash CHAR(64) NOT NULL,
|
|
229
|
+
source_mimetype VARCHAR(64) NULL,
|
|
230
|
+
upload_status ENUM('pending', 'processing', 'done', 'failed') NOT NULL DEFAULT 'pending',
|
|
231
|
+
sticker_id CHAR(36) NULL,
|
|
232
|
+
error_code VARCHAR(64) NULL,
|
|
233
|
+
error_message VARCHAR(255) NULL,
|
|
234
|
+
attempt_count INT UNSIGNED NOT NULL DEFAULT 0,
|
|
235
|
+
last_attempt_at TIMESTAMP NULL DEFAULT NULL,
|
|
236
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
237
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
238
|
+
UNIQUE KEY uq_sticker_pack_web_upload_pack_upload_id (pack_id, upload_id),
|
|
239
|
+
UNIQUE KEY uq_sticker_pack_web_upload_pack_hash (pack_id, sticker_hash),
|
|
240
|
+
INDEX idx_sticker_pack_web_upload_pack_status (pack_id, upload_status),
|
|
241
|
+
INDEX idx_sticker_pack_web_upload_pack_updated (pack_id, updated_at),
|
|
242
|
+
CONSTRAINT fk_sticker_pack_web_upload_pack
|
|
243
|
+
FOREIGN KEY (pack_id) REFERENCES ${TABLES.STICKER_PACK}(id)
|
|
244
|
+
ON DELETE CASCADE ON UPDATE CASCADE,
|
|
245
|
+
CONSTRAINT fk_sticker_pack_web_upload_sticker
|
|
246
|
+
FOREIGN KEY (sticker_id) REFERENCES ${TABLES.STICKER_ASSET}(id)
|
|
247
|
+
ON DELETE SET NULL ON UPDATE CASCADE
|
|
248
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
249
|
+
`;
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Tabela STICKER_ASSET_CLASSIFICATION
|
|
253
|
+
* Classificação de conteúdo por asset (CLIP), com score completo.
|
|
254
|
+
*/
|
|
255
|
+
const createStickerAssetClassificationTableSQL = `
|
|
256
|
+
CREATE TABLE IF NOT EXISTS ${TABLES.STICKER_ASSET_CLASSIFICATION} (
|
|
257
|
+
asset_id CHAR(36) PRIMARY KEY,
|
|
258
|
+
provider VARCHAR(64) NOT NULL DEFAULT 'clip',
|
|
259
|
+
model_name VARCHAR(120) NULL,
|
|
260
|
+
classification_version VARCHAR(32) NOT NULL DEFAULT 'v1',
|
|
261
|
+
category VARCHAR(120) NULL,
|
|
262
|
+
confidence DECIMAL(6,5) NULL,
|
|
263
|
+
nsfw_score DECIMAL(6,5) NULL,
|
|
264
|
+
is_nsfw TINYINT(1) NOT NULL DEFAULT 0,
|
|
265
|
+
all_scores JSON NULL,
|
|
266
|
+
classified_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
267
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
268
|
+
CONSTRAINT fk_sticker_asset_classification_asset
|
|
269
|
+
FOREIGN KEY (asset_id) REFERENCES ${TABLES.STICKER_ASSET}(id)
|
|
270
|
+
ON DELETE CASCADE ON UPDATE CASCADE,
|
|
271
|
+
INDEX idx_sticker_asset_classification_category (category),
|
|
272
|
+
INDEX idx_sticker_asset_classification_nsfw (is_nsfw)
|
|
273
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
274
|
+
`;
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Tabela STICKER_PACK_ENGAGEMENT
|
|
278
|
+
* Armazena métricas reais de interação no catálogo web (cliques/likes/dislikes).
|
|
279
|
+
*/
|
|
280
|
+
const createStickerPackEngagementTableSQL = `
|
|
281
|
+
CREATE TABLE IF NOT EXISTS ${TABLES.STICKER_PACK_ENGAGEMENT} (
|
|
282
|
+
pack_id CHAR(36) PRIMARY KEY,
|
|
283
|
+
open_count BIGINT UNSIGNED NOT NULL DEFAULT 0,
|
|
284
|
+
like_count BIGINT UNSIGNED NOT NULL DEFAULT 0,
|
|
285
|
+
dislike_count BIGINT UNSIGNED NOT NULL DEFAULT 0,
|
|
286
|
+
last_opened_at TIMESTAMP NULL DEFAULT NULL,
|
|
287
|
+
last_interacted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
288
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
289
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
290
|
+
CONSTRAINT fk_sticker_pack_engagement_pack
|
|
291
|
+
FOREIGN KEY (pack_id) REFERENCES ${TABLES.STICKER_PACK}(id)
|
|
292
|
+
ON DELETE CASCADE ON UPDATE CASCADE,
|
|
293
|
+
INDEX idx_sticker_pack_engagement_updated (updated_at),
|
|
294
|
+
INDEX idx_sticker_pack_engagement_like (like_count),
|
|
295
|
+
INDEX idx_sticker_pack_engagement_open (open_count)
|
|
296
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
297
|
+
`;
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Tabela STICKER_PACK_INTERACTION_EVENT
|
|
301
|
+
* Histórico de interações para tendência, recomendação e perfis de criador.
|
|
302
|
+
*/
|
|
303
|
+
const createStickerPackInteractionEventTableSQL = `
|
|
304
|
+
CREATE TABLE IF NOT EXISTS ${TABLES.STICKER_PACK_INTERACTION_EVENT} (
|
|
305
|
+
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
|
|
306
|
+
pack_id CHAR(36) NOT NULL,
|
|
307
|
+
interaction ENUM('open', 'like', 'dislike') NOT NULL,
|
|
308
|
+
actor_key VARCHAR(120) NULL,
|
|
309
|
+
session_key VARCHAR(120) NULL,
|
|
310
|
+
source VARCHAR(32) NULL,
|
|
311
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
312
|
+
CONSTRAINT fk_sticker_pack_interaction_pack
|
|
313
|
+
FOREIGN KEY (pack_id) REFERENCES ${TABLES.STICKER_PACK}(id)
|
|
314
|
+
ON DELETE CASCADE ON UPDATE CASCADE,
|
|
315
|
+
INDEX idx_sticker_pack_interaction_pack_created (pack_id, created_at),
|
|
316
|
+
INDEX idx_sticker_pack_interaction_actor_created (actor_key, created_at),
|
|
317
|
+
INDEX idx_sticker_pack_interaction_session_created (session_key, created_at),
|
|
318
|
+
INDEX idx_sticker_pack_interaction_type_created (interaction, created_at)
|
|
319
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
320
|
+
`;
|
|
321
|
+
|
|
322
|
+
const createSchemaMigrationsTableSQL = `
|
|
323
|
+
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
324
|
+
name VARCHAR(255) PRIMARY KEY,
|
|
325
|
+
executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
326
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
327
|
+
`;
|
|
328
|
+
|
|
329
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
330
|
+
const __dirname = path.dirname(__filename);
|
|
331
|
+
|
|
332
|
+
const MIGRATIONS_DIR = path.join(__dirname, 'migrations');
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Divide um arquivo SQL em statements individuais.
|
|
336
|
+
* Suporta aspas simples e duplas para não quebrar strings.
|
|
337
|
+
*
|
|
338
|
+
* @param {string} sql
|
|
339
|
+
* @returns {string[]}
|
|
340
|
+
*/
|
|
341
|
+
function splitSqlStatements(sql) {
|
|
342
|
+
const statements = [];
|
|
343
|
+
let current = '';
|
|
344
|
+
let inSingleQuote = false;
|
|
345
|
+
let inDoubleQuote = false;
|
|
346
|
+
let escaped = false;
|
|
347
|
+
|
|
348
|
+
for (const char of sql) {
|
|
349
|
+
if (escaped) {
|
|
350
|
+
current += char;
|
|
351
|
+
escaped = false;
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (char === '\\') {
|
|
356
|
+
current += char;
|
|
357
|
+
escaped = true;
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (char === "'" && !inDoubleQuote) {
|
|
362
|
+
inSingleQuote = !inSingleQuote;
|
|
363
|
+
current += char;
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (char === '"' && !inSingleQuote) {
|
|
368
|
+
inDoubleQuote = !inDoubleQuote;
|
|
369
|
+
current += char;
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (char === ';' && !inSingleQuote && !inDoubleQuote) {
|
|
374
|
+
const statement = current.trim();
|
|
375
|
+
if (statement) {
|
|
376
|
+
statements.push(statement);
|
|
377
|
+
}
|
|
378
|
+
current = '';
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
current += char;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const trailing = current.trim();
|
|
386
|
+
if (trailing) {
|
|
387
|
+
statements.push(trailing);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return statements;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Executa migrações SQL idempotentes a partir de `database/migrations`.
|
|
395
|
+
*
|
|
396
|
+
* @param {import('mysql2/promise').Connection} connection
|
|
397
|
+
* @returns {Promise<number>} Quantidade de migrações aplicadas.
|
|
398
|
+
*/
|
|
399
|
+
async function runSqlMigrations(connection) {
|
|
400
|
+
await connection.query(createSchemaMigrationsTableSQL);
|
|
401
|
+
|
|
402
|
+
let files = [];
|
|
403
|
+
try {
|
|
404
|
+
files = await fs.readdir(MIGRATIONS_DIR);
|
|
405
|
+
} catch (error) {
|
|
406
|
+
if (error.code === 'ENOENT') {
|
|
407
|
+
logger.info('Diretório de migrações não encontrado. Seguindo sem migrações extras.');
|
|
408
|
+
return 0;
|
|
409
|
+
}
|
|
410
|
+
throw error;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const migrationFiles = files.filter((file) => file.endsWith('.sql')).sort();
|
|
414
|
+
if (migrationFiles.length === 0) return 0;
|
|
415
|
+
|
|
416
|
+
const [rows] = await connection.query('SELECT name FROM schema_migrations');
|
|
417
|
+
const applied = new Set((rows || []).map((row) => row.name));
|
|
418
|
+
|
|
419
|
+
let appliedCount = 0;
|
|
420
|
+
|
|
421
|
+
for (const fileName of migrationFiles) {
|
|
422
|
+
if (applied.has(fileName)) continue;
|
|
423
|
+
|
|
424
|
+
const filePath = path.join(MIGRATIONS_DIR, fileName);
|
|
425
|
+
const sqlContent = await fs.readFile(filePath, 'utf8');
|
|
426
|
+
const statements = splitSqlStatements(sqlContent);
|
|
427
|
+
|
|
428
|
+
if (statements.length === 0) {
|
|
429
|
+
await connection.query('INSERT INTO schema_migrations (name) VALUES (?)', [fileName]);
|
|
430
|
+
appliedCount += 1;
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
logger.info(`Aplicando migração SQL: ${fileName}`);
|
|
435
|
+
for (const statement of statements) {
|
|
436
|
+
await connection.query(statement);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
await connection.query('INSERT INTO schema_migrations (name) VALUES (?)', [fileName]);
|
|
440
|
+
appliedCount += 1;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return appliedCount;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Inicializa o banco de dados:
|
|
448
|
+
* 1) Conecta ao MySQL sem database
|
|
449
|
+
* 2) Cria o banco se não existir
|
|
450
|
+
* 3) Muda para o database correto
|
|
451
|
+
* 4) Cria todas as tabelas
|
|
452
|
+
* 5) Encerra a conexão
|
|
453
|
+
*
|
|
454
|
+
* Este script é idempotente:
|
|
455
|
+
* pode ser rodado várias vezes sem quebrar nada.
|
|
456
|
+
*
|
|
457
|
+
* @returns {Promise<void>}
|
|
458
|
+
*/
|
|
459
|
+
export default async function initializeDatabase() {
|
|
460
|
+
let connection;
|
|
461
|
+
|
|
462
|
+
try {
|
|
463
|
+
connection = await mysql.createConnection({
|
|
464
|
+
host: dbConfig.host,
|
|
465
|
+
user: dbConfig.user,
|
|
466
|
+
password: dbConfig.password,
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
// Cria/verifica o banco
|
|
470
|
+
await connection.query(createDatabaseSQL);
|
|
471
|
+
logger.info(`Banco de dados '${dbToCreate}' verificado/criado com sucesso.`);
|
|
472
|
+
|
|
473
|
+
// Seleciona o banco recém-criado
|
|
474
|
+
await connection.changeUser({ database: dbToCreate });
|
|
475
|
+
|
|
476
|
+
// Cria todas as tabelas em paralelo
|
|
477
|
+
await Promise.all([
|
|
478
|
+
connection.query(createMessagesTableSQL),
|
|
479
|
+
connection.query(createChatsTableSQL),
|
|
480
|
+
connection.query(createGroupsMetadataTableSQL),
|
|
481
|
+
connection.query(createGroupConfigsTableSQL),
|
|
482
|
+
connection.query(createLidMapTableSQL),
|
|
483
|
+
connection.query(createStickerAssetTableSQL),
|
|
484
|
+
]);
|
|
485
|
+
|
|
486
|
+
// Ordem importa por conta das FKs: pack depende de asset e item depende de pack+asset.
|
|
487
|
+
await connection.query(createStickerPackTableSQL);
|
|
488
|
+
await connection.query(createStickerPackItemTableSQL);
|
|
489
|
+
await connection.query(createStickerPackWebUploadTableSQL);
|
|
490
|
+
await connection.query(createStickerAssetClassificationTableSQL);
|
|
491
|
+
await connection.query(createStickerPackEngagementTableSQL);
|
|
492
|
+
await connection.query(createStickerPackInteractionEventTableSQL);
|
|
493
|
+
|
|
494
|
+
const appliedMigrations = await runSqlMigrations(connection);
|
|
495
|
+
|
|
496
|
+
logger.info('Todas as tabelas foram verificadas/criadas com sucesso.', {
|
|
497
|
+
appliedMigrations,
|
|
498
|
+
});
|
|
499
|
+
} catch (error) {
|
|
500
|
+
logger.error(`Erro ao inicializar o banco: ${error.code || ''} ${error.message}`);
|
|
501
|
+
process.exit(1);
|
|
502
|
+
} finally {
|
|
503
|
+
if (connection) {
|
|
504
|
+
await connection.end();
|
|
505
|
+
logger.info('Conexão com o MySQL encerrada após inicialização.');
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Permite que este arquivo seja executado diretamente:
|
|
512
|
+
* node database/init.js
|
|
513
|
+
*/
|
|
514
|
+
if (process.argv[1] === __filename) {
|
|
515
|
+
initializeDatabase();
|
|
516
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS sticker_asset (
|
|
2
|
+
id CHAR(36) PRIMARY KEY,
|
|
3
|
+
owner_jid VARCHAR(255) NOT NULL,
|
|
4
|
+
sha256 CHAR(64) NOT NULL,
|
|
5
|
+
mimetype VARCHAR(64) NOT NULL,
|
|
6
|
+
is_animated TINYINT(1) NOT NULL DEFAULT 0,
|
|
7
|
+
width INT UNSIGNED NULL,
|
|
8
|
+
height INT UNSIGNED NULL,
|
|
9
|
+
size_bytes INT UNSIGNED NOT NULL,
|
|
10
|
+
storage_path VARCHAR(1024) NOT NULL,
|
|
11
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
12
|
+
UNIQUE KEY uq_sticker_asset_sha256 (sha256),
|
|
13
|
+
INDEX idx_sticker_asset_owner_created (owner_jid, created_at)
|
|
14
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
15
|
+
|
|
16
|
+
CREATE TABLE IF NOT EXISTS sticker_pack (
|
|
17
|
+
id CHAR(36) PRIMARY KEY,
|
|
18
|
+
owner_jid VARCHAR(255) NOT NULL,
|
|
19
|
+
name VARCHAR(120) NOT NULL,
|
|
20
|
+
publisher VARCHAR(120) NOT NULL,
|
|
21
|
+
description TEXT NULL,
|
|
22
|
+
pack_key VARCHAR(160) NOT NULL,
|
|
23
|
+
cover_sticker_id CHAR(36) NULL,
|
|
24
|
+
visibility ENUM('private', 'public', 'unlisted') NOT NULL DEFAULT 'private',
|
|
25
|
+
version INT UNSIGNED NOT NULL DEFAULT 1,
|
|
26
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
27
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
28
|
+
deleted_at TIMESTAMP NULL DEFAULT NULL,
|
|
29
|
+
UNIQUE KEY uq_sticker_pack_pack_key (pack_key),
|
|
30
|
+
INDEX idx_sticker_pack_owner_deleted (owner_jid, deleted_at),
|
|
31
|
+
INDEX idx_sticker_pack_owner_updated (owner_jid, updated_at),
|
|
32
|
+
CONSTRAINT fk_sticker_pack_cover
|
|
33
|
+
FOREIGN KEY (cover_sticker_id) REFERENCES sticker_asset(id)
|
|
34
|
+
ON DELETE SET NULL ON UPDATE CASCADE
|
|
35
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
36
|
+
|
|
37
|
+
CREATE TABLE IF NOT EXISTS sticker_pack_item (
|
|
38
|
+
id CHAR(36) PRIMARY KEY,
|
|
39
|
+
pack_id CHAR(36) NOT NULL,
|
|
40
|
+
sticker_id CHAR(36) NOT NULL,
|
|
41
|
+
position INT UNSIGNED NOT NULL,
|
|
42
|
+
emojis JSON NULL,
|
|
43
|
+
accessibility_label VARCHAR(255) NULL,
|
|
44
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
45
|
+
UNIQUE KEY uq_sticker_pack_item_pack_sticker (pack_id, sticker_id),
|
|
46
|
+
UNIQUE KEY uq_sticker_pack_item_pack_position (pack_id, position),
|
|
47
|
+
INDEX idx_sticker_pack_item_pack_position (pack_id, position),
|
|
48
|
+
CONSTRAINT fk_sticker_pack_item_pack
|
|
49
|
+
FOREIGN KEY (pack_id) REFERENCES sticker_pack(id)
|
|
50
|
+
ON DELETE CASCADE ON UPDATE CASCADE,
|
|
51
|
+
CONSTRAINT fk_sticker_pack_item_asset
|
|
52
|
+
FOREIGN KEY (sticker_id) REFERENCES sticker_asset(id)
|
|
53
|
+
ON DELETE RESTRICT ON UPDATE CASCADE
|
|
54
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS rpg_player (
|
|
2
|
+
jid VARCHAR(255) PRIMARY KEY,
|
|
3
|
+
level INT UNSIGNED NOT NULL DEFAULT 1,
|
|
4
|
+
xp BIGINT UNSIGNED NOT NULL DEFAULT 0,
|
|
5
|
+
gold BIGINT UNSIGNED NOT NULL DEFAULT 200,
|
|
6
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
7
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
|
8
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
9
|
+
|
|
10
|
+
CREATE TABLE IF NOT EXISTS rpg_player_pokemon (
|
|
11
|
+
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
|
|
12
|
+
owner_jid VARCHAR(255) NOT NULL,
|
|
13
|
+
poke_id INT UNSIGNED NOT NULL,
|
|
14
|
+
nickname VARCHAR(120) NULL,
|
|
15
|
+
level INT UNSIGNED NOT NULL DEFAULT 5,
|
|
16
|
+
xp BIGINT UNSIGNED NOT NULL DEFAULT 0,
|
|
17
|
+
current_hp INT UNSIGNED NOT NULL,
|
|
18
|
+
ivs_json JSON NOT NULL,
|
|
19
|
+
moves_json JSON NOT NULL,
|
|
20
|
+
is_active TINYINT(1) NOT NULL DEFAULT 0,
|
|
21
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
22
|
+
INDEX idx_rpg_player_pokemon_owner (owner_jid),
|
|
23
|
+
INDEX idx_rpg_player_pokemon_owner_active (owner_jid, is_active),
|
|
24
|
+
CONSTRAINT fk_rpg_player_pokemon_owner
|
|
25
|
+
FOREIGN KEY (owner_jid) REFERENCES rpg_player(jid)
|
|
26
|
+
ON DELETE CASCADE ON UPDATE CASCADE
|
|
27
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
28
|
+
|
|
29
|
+
CREATE TABLE IF NOT EXISTS rpg_battle_state (
|
|
30
|
+
chat_jid VARCHAR(255) PRIMARY KEY,
|
|
31
|
+
owner_jid VARCHAR(255) NOT NULL,
|
|
32
|
+
my_pokemon_id BIGINT UNSIGNED NOT NULL,
|
|
33
|
+
enemy_snapshot_json JSON NOT NULL,
|
|
34
|
+
turn INT UNSIGNED NOT NULL DEFAULT 1,
|
|
35
|
+
expires_at DATETIME NOT NULL,
|
|
36
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
37
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
38
|
+
UNIQUE KEY uq_rpg_battle_owner (owner_jid),
|
|
39
|
+
INDEX idx_rpg_battle_expires_at (expires_at),
|
|
40
|
+
CONSTRAINT fk_rpg_battle_owner
|
|
41
|
+
FOREIGN KEY (owner_jid) REFERENCES rpg_player(jid)
|
|
42
|
+
ON DELETE CASCADE ON UPDATE CASCADE,
|
|
43
|
+
CONSTRAINT fk_rpg_battle_my_pokemon
|
|
44
|
+
FOREIGN KEY (my_pokemon_id) REFERENCES rpg_player_pokemon(id)
|
|
45
|
+
ON DELETE CASCADE ON UPDATE CASCADE
|
|
46
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
47
|
+
|
|
48
|
+
CREATE TABLE IF NOT EXISTS rpg_player_inventory (
|
|
49
|
+
owner_jid VARCHAR(255) NOT NULL,
|
|
50
|
+
item_key VARCHAR(64) NOT NULL,
|
|
51
|
+
quantity INT UNSIGNED NOT NULL DEFAULT 0,
|
|
52
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
53
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
54
|
+
PRIMARY KEY (owner_jid, item_key),
|
|
55
|
+
CONSTRAINT fk_rpg_inventory_owner
|
|
56
|
+
FOREIGN KEY (owner_jid) REFERENCES rpg_player(jid)
|
|
57
|
+
ON DELETE CASCADE ON UPDATE CASCADE
|
|
58
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
ALTER TABLE rpg_player_pokemon
|
|
2
|
+
ADD COLUMN is_shiny TINYINT(1) NOT NULL DEFAULT 0 AFTER moves_json;
|
|
3
|
+
|
|
4
|
+
CREATE TABLE IF NOT EXISTS rpg_group_biome (
|
|
5
|
+
group_jid VARCHAR(255) PRIMARY KEY,
|
|
6
|
+
biome_key VARCHAR(64) NOT NULL,
|
|
7
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
8
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
|
9
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS rpg_player_mission_progress (
|
|
2
|
+
owner_jid VARCHAR(255) PRIMARY KEY,
|
|
3
|
+
daily_ref_date DATE NOT NULL,
|
|
4
|
+
daily_progress_json JSON NOT NULL,
|
|
5
|
+
daily_claimed_at DATETIME NULL,
|
|
6
|
+
weekly_ref_date DATE NOT NULL,
|
|
7
|
+
weekly_progress_json JSON NOT NULL,
|
|
8
|
+
weekly_claimed_at DATETIME NULL,
|
|
9
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
10
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
11
|
+
CONSTRAINT fk_rpg_mission_owner
|
|
12
|
+
FOREIGN KEY (owner_jid) REFERENCES rpg_player(jid)
|
|
13
|
+
ON DELETE CASCADE ON UPDATE CASCADE
|
|
14
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
ALTER TABLE rpg_player_pokemon
|
|
2
|
+
ADD COLUMN nature_key VARCHAR(64) NULL AFTER moves_json,
|
|
3
|
+
ADD COLUMN ability_key VARCHAR(64) NULL AFTER nature_key,
|
|
4
|
+
ADD COLUMN ability_name VARCHAR(120) NULL AFTER ability_key;
|
|
5
|
+
|
|
6
|
+
CREATE TABLE IF NOT EXISTS rpg_player_pokedex (
|
|
7
|
+
owner_jid VARCHAR(255) NOT NULL,
|
|
8
|
+
poke_id INT UNSIGNED NOT NULL,
|
|
9
|
+
first_captured_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
10
|
+
PRIMARY KEY (owner_jid, poke_id),
|
|
11
|
+
INDEX idx_rpg_pokedex_owner (owner_jid),
|
|
12
|
+
CONSTRAINT fk_rpg_pokedex_owner
|
|
13
|
+
FOREIGN KEY (owner_jid) REFERENCES rpg_player(jid)
|
|
14
|
+
ON DELETE CASCADE ON UPDATE CASCADE
|
|
15
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
16
|
+
|
|
17
|
+
CREATE TABLE IF NOT EXISTS rpg_player_travel (
|
|
18
|
+
owner_jid VARCHAR(255) PRIMARY KEY,
|
|
19
|
+
region_key VARCHAR(120) NULL,
|
|
20
|
+
location_key VARCHAR(120) NULL,
|
|
21
|
+
location_area_key VARCHAR(120) NULL,
|
|
22
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
23
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
24
|
+
CONSTRAINT fk_rpg_travel_owner
|
|
25
|
+
FOREIGN KEY (owner_jid) REFERENCES rpg_player(jid)
|
|
26
|
+
ON DELETE CASCADE ON UPDATE CASCADE
|
|
27
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|