mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-14 07:55:33 +01:00
make mail a service
This commit is contained in:
18
internal/constant/cast.go
Normal file
18
internal/constant/cast.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package constant
|
||||
|
||||
const (
|
||||
BinaryMessageTypeState = 0
|
||||
BinaryMessageTypeChat = 1
|
||||
BinaryMessageTypeQuest = 2
|
||||
BinaryMessageTypeData = 3
|
||||
BinaryMessageTypeMailNotify = 4
|
||||
BinaryMessageTypeEmote = 6
|
||||
)
|
||||
|
||||
// MSG_SYS_CAST[ED]_BINARY broadcast types enum
|
||||
const (
|
||||
BroadcastTypeTargeted = 0x01
|
||||
BroadcastTypeStage = 0x03
|
||||
BroadcastTypeServer = 0x06
|
||||
BroadcastTypeWorld = 0x0a
|
||||
)
|
||||
236
internal/service/mail.go
Normal file
236
internal/service/mail.go
Normal file
@@ -0,0 +1,236 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"erupe-ce/internal/constant"
|
||||
"erupe-ce/network/binpacket"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"erupe-ce/utils/byteframe"
|
||||
"erupe-ce/utils/db"
|
||||
|
||||
"erupe-ce/utils/logger"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type Mail struct {
|
||||
ID int `db:"id"`
|
||||
SenderID uint32 `db:"sender_id"`
|
||||
RecipientID uint32 `db:"recipient_id"`
|
||||
Subject string `db:"subject"`
|
||||
Body string `db:"body"`
|
||||
Read bool `db:"read"`
|
||||
Deleted bool `db:"deleted"`
|
||||
Locked bool `db:"locked"`
|
||||
AttachedItemReceived bool `db:"attached_item_received"`
|
||||
AttachedItemID uint16 `db:"attached_item"`
|
||||
AttachedItemAmount uint16 `db:"attached_item_amount"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
IsGuildInvite bool `db:"is_guild_invite"`
|
||||
IsSystemMessage bool `db:"is_sys_message"`
|
||||
SenderName string `db:"sender_name"`
|
||||
}
|
||||
|
||||
func (m *Mail) Send(transaction *sql.Tx) error {
|
||||
db, err := db.GetDB()
|
||||
logger := logger.Get()
|
||||
|
||||
if err != nil {
|
||||
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
|
||||
}
|
||||
query := `
|
||||
INSERT INTO mail (sender_id, recipient_id, subject, body, attached_item, attached_item_amount, is_guild_invite, is_sys_message)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
`
|
||||
|
||||
if transaction == nil {
|
||||
_, err = db.Exec(query, m.SenderID, m.RecipientID, m.Subject, m.Body, m.AttachedItemID, m.AttachedItemAmount, m.IsGuildInvite, m.IsSystemMessage)
|
||||
} else {
|
||||
_, err = transaction.Exec(query, m.SenderID, m.RecipientID, m.Subject, m.Body, m.AttachedItemID, m.AttachedItemAmount, m.IsGuildInvite, m.IsSystemMessage)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Error(
|
||||
"failed to send mail",
|
||||
zap.Error(err),
|
||||
zap.Uint32("senderID", m.SenderID),
|
||||
zap.Uint32("recipientID", m.RecipientID),
|
||||
zap.String("subject", m.Subject),
|
||||
zap.String("body", m.Body),
|
||||
zap.Uint16("itemID", m.AttachedItemID),
|
||||
zap.Uint16("itemAmount", m.AttachedItemAmount),
|
||||
zap.Bool("isGuildInvite", m.IsGuildInvite),
|
||||
zap.Bool("isSystemMessage", m.IsSystemMessage),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Mail) MarkRead() error {
|
||||
db, err := db.GetDB()
|
||||
logger := logger.Get()
|
||||
if err != nil {
|
||||
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
|
||||
}
|
||||
_, err = db.Exec(`
|
||||
UPDATE mail SET read = true WHERE id = $1
|
||||
`, m.ID)
|
||||
|
||||
if err != nil {
|
||||
logger.Error(
|
||||
"failed to mark mail as read",
|
||||
zap.Error(err),
|
||||
zap.Int("mailID", m.ID),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetMailListForCharacter(charID uint32) ([]Mail, error) {
|
||||
db, err := db.GetDB()
|
||||
logger := logger.Get()
|
||||
if err != nil {
|
||||
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
|
||||
}
|
||||
rows, err := db.Queryx(`
|
||||
SELECT
|
||||
m.id,
|
||||
m.sender_id,
|
||||
m.recipient_id,
|
||||
m.subject,
|
||||
m.read,
|
||||
m.attached_item_received,
|
||||
m.attached_item,
|
||||
m.attached_item_amount,
|
||||
m.created_at,
|
||||
m.is_guild_invite,
|
||||
m.is_sys_message,
|
||||
m.deleted,
|
||||
m.locked,
|
||||
c.name as sender_name
|
||||
FROM mail m
|
||||
JOIN characters c ON c.id = m.sender_id
|
||||
WHERE recipient_id = $1 AND m.deleted = false
|
||||
ORDER BY m.created_at DESC, id DESC
|
||||
LIMIT 32
|
||||
`, charID)
|
||||
|
||||
if err != nil {
|
||||
logger.Error("failed to get mail for character", zap.Error(err), zap.Uint32("charID", charID))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
|
||||
allMail := make([]Mail, 0)
|
||||
|
||||
for rows.Next() {
|
||||
mail := Mail{}
|
||||
|
||||
err := rows.StructScan(&mail)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
allMail = append(allMail, mail)
|
||||
}
|
||||
|
||||
return allMail, nil
|
||||
}
|
||||
|
||||
func GetMailByID(ID int) (*Mail, error) {
|
||||
db, err := db.GetDB()
|
||||
logger := logger.Get()
|
||||
|
||||
if err != nil {
|
||||
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
|
||||
}
|
||||
row := db.QueryRowx(`
|
||||
SELECT
|
||||
m.id,
|
||||
m.sender_id,
|
||||
m.recipient_id,
|
||||
m.subject,
|
||||
m.read,
|
||||
m.body,
|
||||
m.attached_item_received,
|
||||
m.attached_item,
|
||||
m.attached_item_amount,
|
||||
m.created_at,
|
||||
m.is_guild_invite,
|
||||
m.is_sys_message,
|
||||
m.deleted,
|
||||
m.locked,
|
||||
c.name as sender_name
|
||||
FROM mail m
|
||||
JOIN characters c ON c.id = m.sender_id
|
||||
WHERE m.id = $1
|
||||
LIMIT 1
|
||||
`, ID)
|
||||
|
||||
mail := &Mail{}
|
||||
|
||||
err = row.StructScan(mail)
|
||||
|
||||
if err != nil {
|
||||
logger.Error(
|
||||
"failed to retrieve mail",
|
||||
zap.Error(err),
|
||||
zap.Int("mailID", ID),
|
||||
)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mail, nil
|
||||
}
|
||||
|
||||
type SessionMail interface {
|
||||
QueueSendMHF(packet mhfpacket.MHFPacket)
|
||||
}
|
||||
|
||||
func SendMailNotification(s SessionMail, m *Mail, recipient SessionMail) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
notification := &binpacket.MsgBinMailNotify{
|
||||
SenderName: getCharacterName(m.SenderID),
|
||||
}
|
||||
|
||||
notification.Build(bf)
|
||||
|
||||
castedBinary := &mhfpacket.MsgSysCastedBinary{
|
||||
CharID: m.SenderID,
|
||||
BroadcastType: 0x00,
|
||||
MessageType: constant.BinaryMessageTypeMailNotify,
|
||||
RawDataPayload: bf.Data(),
|
||||
}
|
||||
|
||||
castedBinary.Build(bf)
|
||||
|
||||
recipient.QueueSendMHF(castedBinary)
|
||||
}
|
||||
|
||||
func getCharacterName(charID uint32) string {
|
||||
db, err := db.GetDB()
|
||||
logger := logger.Get()
|
||||
|
||||
if err != nil {
|
||||
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
|
||||
}
|
||||
row := db.QueryRow("SELECT name FROM characters WHERE id = $1", charID)
|
||||
|
||||
charName := ""
|
||||
|
||||
err = row.Scan(&charName)
|
||||
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return charName
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"erupe-ce/config"
|
||||
"erupe-ce/internal/constant"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"erupe-ce/utils/byteframe"
|
||||
"erupe-ce/utils/db"
|
||||
@@ -357,7 +358,7 @@ func teleport(s *Session, args []string) error {
|
||||
payload.WriteInt16(int16(y))
|
||||
s.QueueSendMHF(&mhfpacket.MsgSysCastedBinary{
|
||||
CharID: s.CharID,
|
||||
MessageType: BinaryMessageTypeState,
|
||||
MessageType: constant.BinaryMessageTypeState,
|
||||
RawDataPayload: payload.Data(),
|
||||
})
|
||||
s.sendMessage(t("commands.teleport.success", v{"x": fmt.Sprintf("%d", x), "y": fmt.Sprintf("%d", y)}))
|
||||
|
||||
@@ -2,6 +2,7 @@ package channelserver
|
||||
|
||||
import (
|
||||
"erupe-ce/config"
|
||||
"erupe-ce/internal/constant"
|
||||
"erupe-ce/network/binpacket"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"erupe-ce/utils/byteframe"
|
||||
@@ -18,22 +19,6 @@ import (
|
||||
)
|
||||
|
||||
// MSG_SYS_CAST[ED]_BINARY types enum
|
||||
const (
|
||||
BinaryMessageTypeState = 0
|
||||
BinaryMessageTypeChat = 1
|
||||
BinaryMessageTypeQuest = 2
|
||||
BinaryMessageTypeData = 3
|
||||
BinaryMessageTypeMailNotify = 4
|
||||
BinaryMessageTypeEmote = 6
|
||||
)
|
||||
|
||||
// MSG_SYS_CAST[ED]_BINARY broadcast types enum
|
||||
const (
|
||||
BroadcastTypeTargeted = 0x01
|
||||
BroadcastTypeStage = 0x03
|
||||
BroadcastTypeServer = 0x06
|
||||
BroadcastTypeWorld = 0x0a
|
||||
)
|
||||
|
||||
var (
|
||||
commands map[string]config.Command
|
||||
@@ -72,7 +57,7 @@ func sendServerChatMessage(s *Session, message string) {
|
||||
|
||||
castedBin := &mhfpacket.MsgSysCastedBinary{
|
||||
CharID: 0,
|
||||
MessageType: BinaryMessageTypeChat,
|
||||
MessageType: constant.BinaryMessageTypeChat,
|
||||
RawDataPayload: bf.Data(),
|
||||
}
|
||||
|
||||
@@ -118,7 +103,7 @@ func handleMsgSysCastBinary(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
|
||||
var msgBinTargeted *binpacket.MsgBinTargeted
|
||||
var message, author string
|
||||
var returnToSender bool
|
||||
if pkt.MessageType == BinaryMessageTypeChat {
|
||||
if pkt.MessageType == constant.BinaryMessageTypeChat {
|
||||
tmp.SetLE()
|
||||
tmp.Seek(8, 0)
|
||||
message = string(tmp.ReadNullTerminatedBytes())
|
||||
@@ -127,7 +112,7 @@ func handleMsgSysCastBinary(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
|
||||
|
||||
// Customise payload
|
||||
realPayload := pkt.RawDataPayload
|
||||
if pkt.BroadcastType == BroadcastTypeTargeted {
|
||||
if pkt.BroadcastType == constant.BroadcastTypeTargeted {
|
||||
tmp.SetBE()
|
||||
tmp.Seek(0, 0)
|
||||
msgBinTargeted = &binpacket.MsgBinTargeted{}
|
||||
@@ -137,11 +122,11 @@ func handleMsgSysCastBinary(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
|
||||
return
|
||||
}
|
||||
realPayload = msgBinTargeted.RawDataPayload
|
||||
} else if pkt.MessageType == BinaryMessageTypeChat {
|
||||
} else if pkt.MessageType == constant.BinaryMessageTypeChat {
|
||||
if message == "@dice" {
|
||||
returnToSender = true
|
||||
m := binpacket.MsgBinChat{
|
||||
Type: BinaryMessageTypeChat,
|
||||
Type: constant.BinaryMessageTypeChat,
|
||||
Flags: 4,
|
||||
Message: fmt.Sprintf(`%d`, token.RNG.Intn(100)+1),
|
||||
SenderName: author,
|
||||
@@ -162,7 +147,7 @@ func handleMsgSysCastBinary(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
return
|
||||
}
|
||||
if (pkt.BroadcastType == BroadcastTypeStage && s.stage.id == "sl1Ns200p0a0u0") || pkt.BroadcastType == BroadcastTypeWorld {
|
||||
if (pkt.BroadcastType == constant.BroadcastTypeStage && s.stage.id == "sl1Ns200p0a0u0") || pkt.BroadcastType == constant.BroadcastTypeWorld {
|
||||
s.Server.DiscordChannelSend(chatMessage.SenderName, chatMessage.Message)
|
||||
}
|
||||
}
|
||||
@@ -178,15 +163,15 @@ func handleMsgSysCastBinary(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
|
||||
|
||||
// Send to the proper recipients.
|
||||
switch pkt.BroadcastType {
|
||||
case BroadcastTypeWorld:
|
||||
case constant.BroadcastTypeWorld:
|
||||
s.Server.WorldcastMHF(resp, s, nil)
|
||||
case BroadcastTypeStage:
|
||||
case constant.BroadcastTypeStage:
|
||||
if returnToSender {
|
||||
s.stage.BroadcastMHF(resp, nil)
|
||||
} else {
|
||||
s.stage.BroadcastMHF(resp, s)
|
||||
}
|
||||
case BroadcastTypeServer:
|
||||
case constant.BroadcastTypeServer:
|
||||
if pkt.MessageType == 1 {
|
||||
raviSema := s.Server.getRaviSemaphore()
|
||||
if raviSema != nil {
|
||||
@@ -195,7 +180,7 @@ func handleMsgSysCastBinary(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
|
||||
} else {
|
||||
s.Server.BroadcastMHF(resp, s)
|
||||
}
|
||||
case BroadcastTypeTargeted:
|
||||
case constant.BroadcastTypeTargeted:
|
||||
for _, targetID := range (*msgBinTargeted).TargetCharIDs {
|
||||
char := s.Server.FindSessionByCharID(targetID)
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"errors"
|
||||
"erupe-ce/config"
|
||||
"erupe-ce/internal/model"
|
||||
"erupe-ce/internal/service"
|
||||
|
||||
"erupe-ce/utils/db"
|
||||
"erupe-ce/utils/gametime"
|
||||
"erupe-ce/utils/mhfitem"
|
||||
@@ -723,13 +725,13 @@ func HandleMsgMhfOperateGuild(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
|
||||
if err != nil {
|
||||
response = 0
|
||||
} else {
|
||||
mail := Mail{
|
||||
mail := service.Mail{
|
||||
RecipientID: s.CharID,
|
||||
Subject: "Withdrawal",
|
||||
Body: fmt.Sprintf("You have withdrawn from 「%s」.", guild.Name),
|
||||
IsSystemMessage: true,
|
||||
}
|
||||
mail.Send(s, nil)
|
||||
mail.Send(nil)
|
||||
}
|
||||
bf.WriteUint32(uint32(response))
|
||||
case mhfpacket.OperateGuildDonateRank:
|
||||
@@ -900,11 +902,11 @@ func HandleMsgMhfOperateGuildMember(s *Session, db *sqlx.DB, p mhfpacket.MHFPack
|
||||
return
|
||||
}
|
||||
|
||||
var mail Mail
|
||||
var mail service.Mail
|
||||
switch pkt.Action {
|
||||
case mhfpacket.OPERATE_GUILD_MEMBER_ACTION_ACCEPT:
|
||||
err = guild.AcceptApplication(s, pkt.CharID)
|
||||
mail = Mail{
|
||||
mail = service.Mail{
|
||||
RecipientID: pkt.CharID,
|
||||
Subject: "Accepted!",
|
||||
Body: fmt.Sprintf("Your application to join 「%s」 was accepted.", guild.Name),
|
||||
@@ -912,7 +914,7 @@ func HandleMsgMhfOperateGuildMember(s *Session, db *sqlx.DB, p mhfpacket.MHFPack
|
||||
}
|
||||
case mhfpacket.OPERATE_GUILD_MEMBER_ACTION_REJECT:
|
||||
err = guild.RejectApplication(s, pkt.CharID)
|
||||
mail = Mail{
|
||||
mail = service.Mail{
|
||||
RecipientID: pkt.CharID,
|
||||
Subject: "Rejected",
|
||||
Body: fmt.Sprintf("Your application to join 「%s」 was rejected.", guild.Name),
|
||||
@@ -920,7 +922,7 @@ func HandleMsgMhfOperateGuildMember(s *Session, db *sqlx.DB, p mhfpacket.MHFPack
|
||||
}
|
||||
case mhfpacket.OPERATE_GUILD_MEMBER_ACTION_KICK:
|
||||
err = guild.RemoveCharacter(s, pkt.CharID)
|
||||
mail = Mail{
|
||||
mail = service.Mail{
|
||||
RecipientID: pkt.CharID,
|
||||
Subject: "Kicked",
|
||||
Body: fmt.Sprintf("You were kicked from 「%s」.", guild.Name),
|
||||
@@ -934,11 +936,11 @@ func HandleMsgMhfOperateGuildMember(s *Session, db *sqlx.DB, p mhfpacket.MHFPack
|
||||
if err != nil {
|
||||
s.DoAckSimpleFail(pkt.AckHandle, make([]byte, 4))
|
||||
} else {
|
||||
mail.Send(s, nil)
|
||||
mail.Send(nil)
|
||||
for _, channel := range s.Server.Channels {
|
||||
for _, session := range channel.sessions {
|
||||
if session.CharID == pkt.CharID {
|
||||
SendMailNotification(s, &mail, session)
|
||||
service.SendMailNotification(s, &mail, session)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"erupe-ce/internal/service"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"erupe-ce/utils/byteframe"
|
||||
"erupe-ce/utils/gametime"
|
||||
@@ -60,7 +61,7 @@ func HandleMsgMhfPostGuildScout(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
mail := &Mail{
|
||||
mail := &service.Mail{
|
||||
SenderID: s.CharID,
|
||||
RecipientID: pkt.CharID,
|
||||
Subject: s.Server.i18n.guild.invite.title,
|
||||
@@ -71,7 +72,7 @@ func HandleMsgMhfPostGuildScout(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket)
|
||||
IsGuildInvite: true,
|
||||
}
|
||||
|
||||
err = mail.Send(s, transaction)
|
||||
err = mail.Send(transaction)
|
||||
|
||||
if err != nil {
|
||||
rollbackTransaction(s, transaction)
|
||||
@@ -144,16 +145,16 @@ func HandleMsgMhfAnswerGuildScout(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket
|
||||
return
|
||||
}
|
||||
|
||||
var mail []Mail
|
||||
var mail []service.Mail
|
||||
if pkt.Answer {
|
||||
err = guild.AcceptApplication(s, s.CharID)
|
||||
mail = append(mail, Mail{
|
||||
mail = append(mail, service.Mail{
|
||||
RecipientID: s.CharID,
|
||||
Subject: s.Server.i18n.guild.invite.success.title,
|
||||
Body: fmt.Sprintf(s.Server.i18n.guild.invite.success.body, guild.Name),
|
||||
IsSystemMessage: true,
|
||||
})
|
||||
mail = append(mail, Mail{
|
||||
mail = append(mail, service.Mail{
|
||||
SenderID: s.CharID,
|
||||
RecipientID: pkt.LeaderID,
|
||||
Subject: s.Server.i18n.guild.invite.accepted.title,
|
||||
@@ -162,13 +163,13 @@ func HandleMsgMhfAnswerGuildScout(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket
|
||||
})
|
||||
} else {
|
||||
err = guild.RejectApplication(s, s.CharID)
|
||||
mail = append(mail, Mail{
|
||||
mail = append(mail, service.Mail{
|
||||
RecipientID: s.CharID,
|
||||
Subject: s.Server.i18n.guild.invite.rejected.title,
|
||||
Body: fmt.Sprintf(s.Server.i18n.guild.invite.rejected.body, guild.Name),
|
||||
IsSystemMessage: true,
|
||||
})
|
||||
mail = append(mail, Mail{
|
||||
mail = append(mail, service.Mail{
|
||||
SenderID: s.CharID,
|
||||
RecipientID: pkt.LeaderID,
|
||||
Subject: s.Server.i18n.guild.invite.declined.title,
|
||||
@@ -185,7 +186,7 @@ func HandleMsgMhfAnswerGuildScout(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket
|
||||
bf.WriteUint32(guild.ID)
|
||||
s.DoAckBufSucceed(pkt.AckHandle, bf.Data())
|
||||
for _, m := range mail {
|
||||
m.Send(s, nil)
|
||||
m.Send(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,228 +1,15 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"erupe-ce/utils/db"
|
||||
"erupe-ce/internal/service"
|
||||
"erupe-ce/utils/stringsupport"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"erupe-ce/network/binpacket"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"erupe-ce/utils/byteframe"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type Mail struct {
|
||||
ID int `db:"id"`
|
||||
SenderID uint32 `db:"sender_id"`
|
||||
RecipientID uint32 `db:"recipient_id"`
|
||||
Subject string `db:"subject"`
|
||||
Body string `db:"body"`
|
||||
Read bool `db:"read"`
|
||||
Deleted bool `db:"deleted"`
|
||||
Locked bool `db:"locked"`
|
||||
AttachedItemReceived bool `db:"attached_item_received"`
|
||||
AttachedItemID uint16 `db:"attached_item"`
|
||||
AttachedItemAmount uint16 `db:"attached_item_amount"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
IsGuildInvite bool `db:"is_guild_invite"`
|
||||
IsSystemMessage bool `db:"is_sys_message"`
|
||||
SenderName string `db:"sender_name"`
|
||||
}
|
||||
|
||||
func (m *Mail) Send(s *Session, transaction *sql.Tx) error {
|
||||
db, err := db.GetDB()
|
||||
if err != nil {
|
||||
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
|
||||
}
|
||||
query := `
|
||||
INSERT INTO mail (sender_id, recipient_id, subject, body, attached_item, attached_item_amount, is_guild_invite, is_sys_message)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
`
|
||||
|
||||
if transaction == nil {
|
||||
_, err = db.Exec(query, m.SenderID, m.RecipientID, m.Subject, m.Body, m.AttachedItemID, m.AttachedItemAmount, m.IsGuildInvite, m.IsSystemMessage)
|
||||
} else {
|
||||
_, err = transaction.Exec(query, m.SenderID, m.RecipientID, m.Subject, m.Body, m.AttachedItemID, m.AttachedItemAmount, m.IsGuildInvite, m.IsSystemMessage)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
s.Logger.Error(
|
||||
"failed to send mail",
|
||||
zap.Error(err),
|
||||
zap.Uint32("senderID", m.SenderID),
|
||||
zap.Uint32("recipientID", m.RecipientID),
|
||||
zap.String("subject", m.Subject),
|
||||
zap.String("body", m.Body),
|
||||
zap.Uint16("itemID", m.AttachedItemID),
|
||||
zap.Uint16("itemAmount", m.AttachedItemAmount),
|
||||
zap.Bool("isGuildInvite", m.IsGuildInvite),
|
||||
zap.Bool("isSystemMessage", m.IsSystemMessage),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Mail) MarkRead(s *Session) error {
|
||||
db, err := db.GetDB()
|
||||
if err != nil {
|
||||
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
|
||||
}
|
||||
_, err = db.Exec(`
|
||||
UPDATE mail SET read = true WHERE id = $1
|
||||
`, m.ID)
|
||||
|
||||
if err != nil {
|
||||
s.Logger.Error(
|
||||
"failed to mark mail as read",
|
||||
zap.Error(err),
|
||||
zap.Int("mailID", m.ID),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetMailListForCharacter(s *Session, charID uint32) ([]Mail, error) {
|
||||
db, err := db.GetDB()
|
||||
if err != nil {
|
||||
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
|
||||
}
|
||||
rows, err := db.Queryx(`
|
||||
SELECT
|
||||
m.id,
|
||||
m.sender_id,
|
||||
m.recipient_id,
|
||||
m.subject,
|
||||
m.read,
|
||||
m.attached_item_received,
|
||||
m.attached_item,
|
||||
m.attached_item_amount,
|
||||
m.created_at,
|
||||
m.is_guild_invite,
|
||||
m.is_sys_message,
|
||||
m.deleted,
|
||||
m.locked,
|
||||
c.name as sender_name
|
||||
FROM mail m
|
||||
JOIN characters c ON c.id = m.sender_id
|
||||
WHERE recipient_id = $1 AND m.deleted = false
|
||||
ORDER BY m.created_at DESC, id DESC
|
||||
LIMIT 32
|
||||
`, charID)
|
||||
|
||||
if err != nil {
|
||||
s.Logger.Error("failed to get mail for character", zap.Error(err), zap.Uint32("charID", charID))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
|
||||
allMail := make([]Mail, 0)
|
||||
|
||||
for rows.Next() {
|
||||
mail := Mail{}
|
||||
|
||||
err := rows.StructScan(&mail)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
allMail = append(allMail, mail)
|
||||
}
|
||||
|
||||
return allMail, nil
|
||||
}
|
||||
|
||||
func GetMailByID(s *Session, ID int) (*Mail, error) {
|
||||
db, err := db.GetDB()
|
||||
if err != nil {
|
||||
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
|
||||
}
|
||||
row := db.QueryRowx(`
|
||||
SELECT
|
||||
m.id,
|
||||
m.sender_id,
|
||||
m.recipient_id,
|
||||
m.subject,
|
||||
m.read,
|
||||
m.body,
|
||||
m.attached_item_received,
|
||||
m.attached_item,
|
||||
m.attached_item_amount,
|
||||
m.created_at,
|
||||
m.is_guild_invite,
|
||||
m.is_sys_message,
|
||||
m.deleted,
|
||||
m.locked,
|
||||
c.name as sender_name
|
||||
FROM mail m
|
||||
JOIN characters c ON c.id = m.sender_id
|
||||
WHERE m.id = $1
|
||||
LIMIT 1
|
||||
`, ID)
|
||||
|
||||
mail := &Mail{}
|
||||
|
||||
err = row.StructScan(mail)
|
||||
|
||||
if err != nil {
|
||||
s.Logger.Error(
|
||||
"failed to retrieve mail",
|
||||
zap.Error(err),
|
||||
zap.Int("mailID", ID),
|
||||
)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mail, nil
|
||||
}
|
||||
|
||||
func SendMailNotification(s *Session, m *Mail, recipient *Session) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
notification := &binpacket.MsgBinMailNotify{
|
||||
SenderName: getCharacterName(s, m.SenderID),
|
||||
}
|
||||
|
||||
notification.Build(bf)
|
||||
|
||||
castedBinary := &mhfpacket.MsgSysCastedBinary{
|
||||
CharID: m.SenderID,
|
||||
BroadcastType: 0x00,
|
||||
MessageType: BinaryMessageTypeMailNotify,
|
||||
RawDataPayload: bf.Data(),
|
||||
}
|
||||
|
||||
castedBinary.Build(bf)
|
||||
|
||||
recipient.QueueSendMHF(castedBinary)
|
||||
}
|
||||
|
||||
func getCharacterName(s *Session, charID uint32) string {
|
||||
db, err := db.GetDB()
|
||||
if err != nil {
|
||||
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
|
||||
}
|
||||
row := db.QueryRow("SELECT name FROM characters WHERE id = $1", charID)
|
||||
|
||||
charName := ""
|
||||
|
||||
err = row.Scan(&charName)
|
||||
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return charName
|
||||
}
|
||||
|
||||
func handleMsgMhfReadMail(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfReadMail)
|
||||
|
||||
@@ -232,7 +19,7 @@ func handleMsgMhfReadMail(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
|
||||
return
|
||||
}
|
||||
|
||||
mail, err := GetMailByID(s, mailId)
|
||||
mail, err := service.GetMailByID(mailId)
|
||||
if err != nil {
|
||||
s.DoAckBufSucceed(pkt.AckHandle, []byte{0})
|
||||
return
|
||||
@@ -248,7 +35,7 @@ func handleMsgMhfReadMail(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
|
||||
func handleMsgMhfListMail(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfListMail)
|
||||
|
||||
mail, err := GetMailListForCharacter(s, s.CharID)
|
||||
mail, err := service.GetMailListForCharacter(s.CharID)
|
||||
if err != nil {
|
||||
s.DoAckBufSucceed(pkt.AckHandle, []byte{0})
|
||||
return
|
||||
@@ -317,7 +104,7 @@ func handleMsgMhfListMail(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
|
||||
func handleMsgMhfOprtMail(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfOprtMail)
|
||||
|
||||
mail, err := GetMailByID(s, s.mailList[pkt.AccIndex])
|
||||
mail, err := service.GetMailByID(s.mailList[pkt.AccIndex])
|
||||
if err != nil {
|
||||
s.DoAckSimpleSucceed(pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"erupe-ce/internal/constant"
|
||||
"erupe-ce/network/binpacket"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"erupe-ce/utils/byteframe"
|
||||
@@ -45,7 +46,7 @@ func (server *ChannelServer) BroadcastChatMessage(message string) {
|
||||
msgBinChat.Build(bf)
|
||||
|
||||
server.BroadcastMHF(&mhfpacket.MsgSysCastedBinary{
|
||||
MessageType: BinaryMessageTypeChat,
|
||||
MessageType: constant.BinaryMessageTypeChat,
|
||||
RawDataPayload: bf.Data(),
|
||||
}, nil)
|
||||
}
|
||||
@@ -76,8 +77,8 @@ func (server *ChannelServer) BroadcastRaviente(ip uint32, port uint16, stage []b
|
||||
bf.WriteUint16(0) // Unk
|
||||
bf.WriteBytes(stage)
|
||||
server.WorldcastMHF(&mhfpacket.MsgSysCastedBinary{
|
||||
BroadcastType: BroadcastTypeServer,
|
||||
MessageType: BinaryMessageTypeChat,
|
||||
BroadcastType: constant.BroadcastTypeServer,
|
||||
MessageType: constant.BinaryMessageTypeChat,
|
||||
RawDataPayload: bf.Data(),
|
||||
}, nil, server)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"erupe-ce/config"
|
||||
"erupe-ce/internal/constant"
|
||||
"erupe-ce/network"
|
||||
"erupe-ce/network/binpacket"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
"erupe-ce/utils/stringstack"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -269,7 +271,7 @@ func (s *Session) sendMessage(message string) {
|
||||
msgBinChat.Build(bf)
|
||||
castedBin := &mhfpacket.MsgSysCastedBinary{
|
||||
CharID: 0,
|
||||
MessageType: BinaryMessageTypeChat,
|
||||
MessageType: constant.BinaryMessageTypeChat,
|
||||
RawDataPayload: bf.Data(),
|
||||
}
|
||||
s.QueueSendMHF(castedBin)
|
||||
|
||||
Reference in New Issue
Block a user