feat: add chat for backend
This commit is contained in:
@@ -106,6 +106,8 @@ func main() {
|
|||||||
r.Get("/shop", ecoH.Shop)
|
r.Get("/shop", ecoH.Shop)
|
||||||
r.Post("/shop/buy-card", ecoH.BuyCard)
|
r.Post("/shop/buy-card", ecoH.BuyCard)
|
||||||
r.Post("/shop/select-card", ecoH.SelectCard)
|
r.Post("/shop/select-card", ecoH.SelectCard)
|
||||||
|
r.Get("/chat-packs", ecoH.ChatPacks)
|
||||||
|
r.Post("/shop/buy-chat-pack", ecoH.BuyChatPack)
|
||||||
r.Post("/shop/purchase", ecoH.Purchase)
|
r.Post("/shop/purchase", ecoH.Purchase)
|
||||||
r.Post("/rewards/daily", ecoH.Daily)
|
r.Post("/rewards/daily", ecoH.Daily)
|
||||||
r.Post("/rewards/ad", ecoH.AdReward)
|
r.Post("/rewards/ad", ecoH.AdReward)
|
||||||
|
|||||||
@@ -51,6 +51,11 @@ func (h *Handler) Routes(user, pass string) http.Handler {
|
|||||||
r.Post("/users/coins", h.adjustCoins)
|
r.Post("/users/coins", h.adjustCoins)
|
||||||
r.Post("/users/grant", h.grantToUser)
|
r.Post("/users/grant", h.grantToUser)
|
||||||
r.Post("/season/reset", h.resetSeason)
|
r.Post("/season/reset", h.resetSeason)
|
||||||
|
r.Get("/chat", h.chatAdmin)
|
||||||
|
r.Post("/chat/pack/add", h.chatPackAdd)
|
||||||
|
r.Post("/chat/pack/delete", h.chatPackDelete)
|
||||||
|
r.Post("/chat/message/add", h.chatMessageAdd)
|
||||||
|
r.Post("/chat/message/delete", h.chatMessageDelete)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,6 +443,103 @@ func (h *Handler) grantToUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// rows یک کوئری را به []map[string]any تبدیل میکند (برای رندرِ عمومیِ جدولها).
|
// rows یک کوئری را به []map[string]any تبدیل میکند (برای رندرِ عمومیِ جدولها).
|
||||||
|
// --- مدیریتِ بستههای چت و پیامها ---
|
||||||
|
|
||||||
|
func (h *Handler) chatAdmin(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
packs := h.rows(ctx,
|
||||||
|
`SELECT id,title,kind,price_coins,vip,sort,enabled FROM chat_packs ORDER BY sort`,
|
||||||
|
"id", "title", "kind", "price_coins", "vip", "sort", "enabled")
|
||||||
|
// پیامهای هر بسته را زیرِ همان بسته قرار میدهیم.
|
||||||
|
for _, p := range packs {
|
||||||
|
id, _ := p["id"].(string)
|
||||||
|
p["messages"] = h.messagesOf(ctx, id)
|
||||||
|
}
|
||||||
|
h.render(w, "chat.html", map[string]any{
|
||||||
|
"Nav": "chat",
|
||||||
|
"Packs": packs,
|
||||||
|
"Saved": r.URL.Query().Get("saved") == "1",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) messagesOf(ctx context.Context, packID string) []map[string]any {
|
||||||
|
rs, err := h.db.QueryContext(ctx,
|
||||||
|
`SELECT id,body,sort FROM chat_messages WHERE pack_id=? ORDER BY sort,id`, packID)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer rs.Close()
|
||||||
|
var out []map[string]any
|
||||||
|
for rs.Next() {
|
||||||
|
var id, sort int
|
||||||
|
var body string
|
||||||
|
if err := rs.Scan(&id, &body, &sort); err != nil {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
out = append(out, map[string]any{"id": id, "body": body, "sort": sort})
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) chatPackAdd(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_ = r.ParseForm()
|
||||||
|
id := r.FormValue("id")
|
||||||
|
if id == "" {
|
||||||
|
http.Error(w, "id required", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
num := func(k string) int { v, _ := strconv.Atoi(r.FormValue(k)); return v }
|
||||||
|
vip := 0
|
||||||
|
if r.FormValue("vip") == "on" {
|
||||||
|
vip = 1
|
||||||
|
}
|
||||||
|
kind := r.FormValue("kind")
|
||||||
|
if kind != "emoji" {
|
||||||
|
kind = "text"
|
||||||
|
}
|
||||||
|
_, err := h.db.ExecContext(r.Context(),
|
||||||
|
`INSERT OR REPLACE INTO chat_packs (id,title,kind,price_coins,vip,sort,enabled) VALUES (?,?,?,?,?,?,1)`,
|
||||||
|
id, r.FormValue("title"), kind, num("price_coins"), vip, num("sort"))
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, "/admin/chat?saved=1", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) chatPackDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_ = r.ParseForm()
|
||||||
|
id := r.FormValue("id")
|
||||||
|
_, _ = h.db.ExecContext(r.Context(), `DELETE FROM chat_messages WHERE pack_id=?`, id)
|
||||||
|
_, _ = h.db.ExecContext(r.Context(), `DELETE FROM chat_packs WHERE id=?`, id)
|
||||||
|
http.Redirect(w, r, "/admin/chat?saved=1", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) chatMessageAdd(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_ = r.ParseForm()
|
||||||
|
pack := r.FormValue("pack_id")
|
||||||
|
body := r.FormValue("body")
|
||||||
|
if pack == "" || body == "" {
|
||||||
|
http.Redirect(w, r, "/admin/chat", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sort, _ := strconv.Atoi(r.FormValue("sort"))
|
||||||
|
_, err := h.db.ExecContext(r.Context(),
|
||||||
|
`INSERT INTO chat_messages (pack_id,body,sort) VALUES (?,?,?)`, pack, body, sort)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, "/admin/chat?saved=1", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) chatMessageDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_ = r.ParseForm()
|
||||||
|
id, _ := strconv.Atoi(r.FormValue("id"))
|
||||||
|
_, _ = h.db.ExecContext(r.Context(), `DELETE FROM chat_messages WHERE id=?`, id)
|
||||||
|
http.Redirect(w, r, "/admin/chat?saved=1", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Handler) rows(ctx context.Context, query string, cols ...string) []map[string]any {
|
func (h *Handler) rows(ctx context.Context, query string, cols ...string) []map[string]any {
|
||||||
rs, err := h.db.QueryContext(ctx, query)
|
rs, err := h.db.QueryContext(ctx, query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html dir="rtl" lang="fa">
|
||||||
|
{{template "head" .}}
|
||||||
|
<body>
|
||||||
|
{{template "nav" .}}
|
||||||
|
<div class="wrap">
|
||||||
|
<h1>بستههای چت</h1>
|
||||||
|
{{if .Saved}}<div class="saved">انجام شد ✓</div>{{end}}
|
||||||
|
|
||||||
|
<p style="color:#9aa;font-size:13px">
|
||||||
|
هر بسته شاملِ چند پیامِ آماده یا شکلک است. بسته با قیمتِ ۰ و بدونِ VIP برای
|
||||||
|
همه رایگان است؛ تیکِ VIP یعنی برای کاربرانِ VIP رایگان (و برای بقیه با قیمتِ سکه).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>افزودن / ویرایش بسته</h2>
|
||||||
|
<table>
|
||||||
|
<tr><th>شناسه</th><th>عنوان</th><th>نوع</th><th>قیمت (سکه)</th><th>VIP رایگان</th><th>ترتیب</th><th></th></tr>
|
||||||
|
{{range .Packs}}
|
||||||
|
<tr><form method="post" action="/admin/chat/pack/add">
|
||||||
|
<td>{{.id}}<input type="hidden" name="id" value="{{.id}}"></td>
|
||||||
|
<td><input name="title" value="{{.title}}"></td>
|
||||||
|
<td>
|
||||||
|
<select name="kind">
|
||||||
|
<option value="text" {{if eq .kind "text"}}selected{{end}}>متن</option>
|
||||||
|
<option value="emoji" {{if eq .kind "emoji"}}selected{{end}}>شکلک</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td><input name="price_coins" value="{{.price_coins}}" style="width:80px"></td>
|
||||||
|
<td><input type="checkbox" name="vip" {{if .vip}}checked{{end}}></td>
|
||||||
|
<td><input name="sort" value="{{.sort}}" style="width:50px"></td>
|
||||||
|
<td>
|
||||||
|
<button>ذخیره</button>
|
||||||
|
<button formaction="/admin/chat/pack/delete" formnovalidate class="del">حذف</button>
|
||||||
|
</td>
|
||||||
|
</form></tr>
|
||||||
|
{{end}}
|
||||||
|
<tr class="addrow"><form method="post" action="/admin/chat/pack/add">
|
||||||
|
<td><input name="id" placeholder="شناسه" required></td>
|
||||||
|
<td><input name="title" placeholder="عنوان"></td>
|
||||||
|
<td><select name="kind"><option value="text">متن</option><option value="emoji">شکلک</option></select></td>
|
||||||
|
<td><input name="price_coins" value="0" style="width:80px"></td>
|
||||||
|
<td><input type="checkbox" name="vip"></td>
|
||||||
|
<td><input name="sort" value="0" style="width:50px"></td>
|
||||||
|
<td><button>افزودن بسته</button></td>
|
||||||
|
</form></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{{range .Packs}}
|
||||||
|
<h2>پیامهای «{{.title}}» <small style="color:#9aa">({{.id}})</small></h2>
|
||||||
|
<table>
|
||||||
|
<tr><th>متن / شکلک</th><th>ترتیب</th><th></th></tr>
|
||||||
|
{{range .messages}}
|
||||||
|
<tr><form method="post" action="/admin/chat/message/delete">
|
||||||
|
<td>{{.body}}</td>
|
||||||
|
<td>{{.sort}}</td>
|
||||||
|
<td>
|
||||||
|
<input type="hidden" name="id" value="{{.id}}">
|
||||||
|
<button class="del">حذف</button>
|
||||||
|
</td>
|
||||||
|
</form></tr>
|
||||||
|
{{end}}
|
||||||
|
<tr class="addrow"><form method="post" action="/admin/chat/message/add">
|
||||||
|
<td><input type="hidden" name="pack_id" value="{{.id}}"><input name="body" placeholder="متن یا شکلک" required style="width:100%"></td>
|
||||||
|
<td><input name="sort" value="0" style="width:50px"></td>
|
||||||
|
<td><button>افزودن پیام</button></td>
|
||||||
|
</form></tr>
|
||||||
|
</table>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -33,6 +33,7 @@
|
|||||||
<nav>
|
<nav>
|
||||||
<a href="/admin" class="{{if eq .Nav "dashboard"}}active{{end}}">داشبورد</a>
|
<a href="/admin" class="{{if eq .Nav "dashboard"}}active{{end}}">داشبورد</a>
|
||||||
<a href="/admin/shop" class="{{if eq .Nav "shop"}}active{{end}}">فروشگاه</a>
|
<a href="/admin/shop" class="{{if eq .Nav "shop"}}active{{end}}">فروشگاه</a>
|
||||||
|
<a href="/admin/chat" class="{{if eq .Nav "chat"}}active{{end}}">چت</a>
|
||||||
<a href="/admin/users" class="{{if eq .Nav "users"}}active{{end}}">کاربران</a>
|
<a href="/admin/users" class="{{if eq .Nav "users"}}active{{end}}">کاربران</a>
|
||||||
</nav>
|
</nav>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
package economy
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// ChatPack یک بستهی پیام/شکلکِ چت. Owned یعنی کاربر میتواند از آن استفاده کند
|
||||||
|
// (رایگان، یا VIP برای بستهی vip، یا خریداریشده).
|
||||||
|
type ChatPack struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Kind string `json:"kind"` // text | emoji
|
||||||
|
PriceCoins int64 `json:"price_coins"`
|
||||||
|
VIP bool `json:"vip"` // برای VIP رایگان است
|
||||||
|
Owned bool `json:"owned"`
|
||||||
|
Messages []string `json:"messages"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChatPacks بستههای فعال را همراه پیامها و وضعیتِ مالکیتِ کاربر برمیگرداند.
|
||||||
|
func (s *Service) ChatPacks(ctx context.Context, userID int64) ([]ChatPack, error) {
|
||||||
|
rows, err := s.db.QueryContext(ctx,
|
||||||
|
`SELECT id, title, kind, price_coins, vip FROM chat_packs WHERE enabled = 1 ORDER BY sort`)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var packs []ChatPack
|
||||||
|
for rows.Next() {
|
||||||
|
var p ChatPack
|
||||||
|
var vip int
|
||||||
|
if err := rows.Scan(&p.ID, &p.Title, &p.Kind, &p.PriceCoins, &vip); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.VIP = vip == 1
|
||||||
|
packs = append(packs, p)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
owned, err := s.ownedChatPacks(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
isVIP := s.isVIP(ctx, userID)
|
||||||
|
|
||||||
|
for i := range packs {
|
||||||
|
p := &packs[i]
|
||||||
|
// رایگان (بدون قیمت و غیرِ vip)، یا VIP برای بستهی vip، یا خریداریشده.
|
||||||
|
p.Owned = (p.PriceCoins == 0 && !p.VIP) || (p.VIP && isVIP) || owned[p.ID]
|
||||||
|
msgs, err := s.packMessages(ctx, p.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.Messages = msgs
|
||||||
|
}
|
||||||
|
return packs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) packMessages(ctx context.Context, packID string) ([]string, error) {
|
||||||
|
rows, err := s.db.QueryContext(ctx,
|
||||||
|
`SELECT body FROM chat_messages WHERE pack_id = ? ORDER BY sort, id`, packID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
msgs := []string{}
|
||||||
|
for rows.Next() {
|
||||||
|
var b string
|
||||||
|
if err := rows.Scan(&b); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
msgs = append(msgs, b)
|
||||||
|
}
|
||||||
|
return msgs, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) ownedChatPacks(ctx context.Context, userID int64) (map[string]bool, error) {
|
||||||
|
rows, err := s.db.QueryContext(ctx,
|
||||||
|
`SELECT pack_id FROM user_chat_packs WHERE user_id = ?`, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
owned := map[string]bool{}
|
||||||
|
for rows.Next() {
|
||||||
|
var id string
|
||||||
|
if err := rows.Scan(&id); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
owned[id] = true
|
||||||
|
}
|
||||||
|
return owned, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuyChatPack یک بستهی چت را با سکه باز میکند. بستههای رایگان/قبلاًمالک خطا
|
||||||
|
// میدهند؛ بستهی VIP برای کاربرِ VIP خودبهخود در دسترس است (نیازی به خرید نیست).
|
||||||
|
func (s *Service) BuyChatPack(ctx context.Context, userID int64, packID string) error {
|
||||||
|
var price int64
|
||||||
|
var vip, enabled int
|
||||||
|
err := s.db.QueryRowContext(ctx,
|
||||||
|
`SELECT price_coins, vip, enabled FROM chat_packs WHERE id = ?`, packID).
|
||||||
|
Scan(&price, &vip, &enabled)
|
||||||
|
if err != nil || enabled != 1 {
|
||||||
|
return ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := s.db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer tx.Rollback()
|
||||||
|
|
||||||
|
var exists int
|
||||||
|
_ = tx.QueryRowContext(ctx,
|
||||||
|
`SELECT 1 FROM user_chat_packs WHERE user_id = ? AND pack_id = ?`, userID, packID).Scan(&exists)
|
||||||
|
if exists == 1 {
|
||||||
|
return ErrAlreadyOwned
|
||||||
|
}
|
||||||
|
if price > 0 {
|
||||||
|
if err := adjustTx(ctx, tx, userID, CurrencyCoin, -price, "buy_chat_pack", packID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err := tx.ExecContext(ctx,
|
||||||
|
`INSERT INTO user_chat_packs (user_id, pack_id) VALUES (?, ?)`, userID, packID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return tx.Commit()
|
||||||
|
}
|
||||||
@@ -87,6 +87,49 @@ func (h *Handler) BuyCard(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChatPacks — GET /api/chat-packs (بستههای پیام/شکلک + مالکیتِ کاربر)
|
||||||
|
func (h *Handler) ChatPacks(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id, ok := uid(r)
|
||||||
|
if !ok {
|
||||||
|
httpx.Error(w, http.StatusUnauthorized, "unauthorized")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
packs, err := h.svc.ChatPacks(r.Context(), id)
|
||||||
|
if err != nil {
|
||||||
|
httpx.Error(w, http.StatusInternalServerError, "server error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
httpx.JSON(w, http.StatusOK, map[string]any{"packs": packs})
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuyChatPack — POST /api/shop/buy-chat-pack
|
||||||
|
func (h *Handler) BuyChatPack(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id, ok := uid(r)
|
||||||
|
if !ok {
|
||||||
|
httpx.Error(w, http.StatusUnauthorized, "unauthorized")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req struct {
|
||||||
|
PackID string `json:"pack_id"`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
|
httpx.Error(w, http.StatusBadRequest, "invalid body")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch err := h.svc.BuyChatPack(r.Context(), id, req.PackID); {
|
||||||
|
case err == nil:
|
||||||
|
httpx.JSON(w, http.StatusOK, map[string]string{"message": "ok"})
|
||||||
|
case errors.Is(err, ErrNotFound):
|
||||||
|
httpx.Error(w, http.StatusNotFound, "pack not found")
|
||||||
|
case errors.Is(err, ErrAlreadyOwned):
|
||||||
|
httpx.Error(w, http.StatusConflict, "already owned")
|
||||||
|
case errors.Is(err, ErrInsufficient):
|
||||||
|
httpx.Error(w, http.StatusPaymentRequired, "insufficient coins")
|
||||||
|
default:
|
||||||
|
httpx.Error(w, http.StatusInternalServerError, "server error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SelectCard — POST /api/shop/select-card
|
// SelectCard — POST /api/shop/select-card
|
||||||
func (h *Handler) SelectCard(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) SelectCard(w http.ResponseWriter, r *http.Request) {
|
||||||
id, ok := uid(r)
|
id, ok := uid(r)
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
-- بستههای چت (پیامهای آماده و شکلکها). برخی رایگان، برخی با سکه یا ویژهی VIP.
|
||||||
|
CREATE TABLE IF NOT EXISTS chat_packs (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
kind TEXT NOT NULL DEFAULT 'text', -- text | emoji
|
||||||
|
price_coins INTEGER NOT NULL DEFAULT 0, -- ۰ یعنی رایگان (مگر vip)
|
||||||
|
vip INTEGER NOT NULL DEFAULT 0, -- ۱ یعنی برای VIP رایگان است
|
||||||
|
sort INTEGER NOT NULL DEFAULT 0,
|
||||||
|
enabled INTEGER NOT NULL DEFAULT 1
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS chat_messages (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
pack_id TEXT NOT NULL,
|
||||||
|
body TEXT NOT NULL, -- متنِ پیام یا کاراکترِ شکلک
|
||||||
|
sort INTEGER NOT NULL DEFAULT 0
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_chat_messages_pack ON chat_messages(pack_id);
|
||||||
|
|
||||||
|
-- مالکیتِ بستههای خریداریشده توسط کاربر.
|
||||||
|
CREATE TABLE IF NOT EXISTS user_chat_packs (
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
pack_id TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (user_id, pack_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO chat_packs (id, title, kind, price_coins, vip, sort) VALUES
|
||||||
|
('general', 'عمومی', 'text', 0, 0, 1),
|
||||||
|
('friendly', 'دوستانه', 'text', 2500, 0, 2),
|
||||||
|
('kolkol', 'کل کل', 'text', 5500, 0, 3),
|
||||||
|
('hakem', 'حکم بازها', 'text', 6500, 0, 4),
|
||||||
|
('emoji', 'شکلک', 'emoji', 1000, 1, 5);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO chat_messages (pack_id, body, sort) VALUES
|
||||||
|
-- عمومی (رایگان)
|
||||||
|
('general','سلام',1),('general','ساکت',2),('general','بعد بازی بیا اتاق چت!',3),
|
||||||
|
('general','عجب شانسی!',4),('general','با شما نبودم',5),('general','رحم کن',6),
|
||||||
|
('general','شرمنده',7),('general','حواسم نبود',8),('general','ای بابا',9),
|
||||||
|
('general','آخیش',10),('general','نت ضعیفه',11),('general','بیا دیگه',12),
|
||||||
|
-- دوستانه
|
||||||
|
('friendly','به به بروبچ با صفا',1),('friendly','دم شما گرم',2),('friendly','خیلی مخلصیم',3),
|
||||||
|
('friendly','به مولا اگه بذارم',4),('friendly','آقایی',5),('friendly','با خاک پاتیم',6),
|
||||||
|
('friendly','فدایی داری',7),('friendly','حال و احوال؟',8),('friendly','سلاطین',9),
|
||||||
|
('friendly','آبجی خودمی',10),('friendly','شماره بدی زنگ میزنم',11),
|
||||||
|
-- کل کل
|
||||||
|
('kolkol','اینکاره نیستی',1),('kolkol','ها ها ها ها',2),('kolkol','خخخخخ...',3),
|
||||||
|
('kolkol','هنوز زوده برات',4),('kolkol','حالتو میگیرم',5),('kolkol','شانستو ببین',6),
|
||||||
|
('kolkol','شاخ نسو',7),('kolkol','بی بی بودی!',8),('kolkol','برو کنار بذار باد بیاد',9),
|
||||||
|
('kolkol','ناخونات نشکنه جیگر',10),('kolkol','چرا میخندی؟',11),('kolkol','یاد گرفتی؟',12),
|
||||||
|
-- حکم بازها
|
||||||
|
('hakem','کارت تمومه',1),('hakem','هیس بابا',2),('hakem','کریم بخدا مسلمون نیستی',3),
|
||||||
|
('hakem','حاکم فقط خودم',4),('hakem','کت نشین! با جواب میخوام',5),('hakem','دست خوبم نداریم',6),
|
||||||
|
('hakem','کمه!',7),('hakem','حواست کجاست؟',8),('hakem','شیرزن',9),
|
||||||
|
('hakem','خیلی عقبید',10),('hakem','برادرمی',11),
|
||||||
|
-- شکلک (emoji)
|
||||||
|
('emoji','😎',1),('emoji','😉',2),('emoji','😌',3),('emoji','😄',4),('emoji','😆',5),
|
||||||
|
('emoji','😡',6),('emoji','😍',7),('emoji','🙄',8),('emoji','😭',9),('emoji','👋',10),
|
||||||
|
('emoji','🤝',11),('emoji','🤔',12),('emoji','😴',13),('emoji','🤑',14),('emoji','😵',15),('emoji','🥳',16);
|
||||||
Reference in New Issue
Block a user