No database

This commit is contained in:
stratic-dev
2024-03-15 00:54:18 +00:00
parent def2bc3d2c
commit 3797438ca2
9 changed files with 94 additions and 81 deletions

2
.gitignore vendored
View File

@@ -8,4 +8,4 @@ savedata/*/
*.lnk *.lnk
*.bat *.bat
/docker/db-data /docker/db-data
sreenshots/* screenshots/*

View File

@@ -13,7 +13,8 @@
"Enabled":true, "Enabled":true,
"Host":"127.0.0.1", "Host":"127.0.0.1",
"Port":8080, "Port":8080,
"OutputDir":"screenshots" "OutputDir":"screenshots",
"UploadQuality":100
}, },
"DeleteOnSaveCorruption": false, "DeleteOnSaveCorruption": false,
"ClientMode": "ZZ", "ClientMode": "ZZ",

View File

@@ -107,10 +107,11 @@ type SaveDumpOptions struct {
} }
type ScreenshotsOptions struct { type ScreenshotsOptions struct {
Enabled bool Enabled bool
Host string // Destination for screenshots uploaded to BBS Host string // Destination for screenshots uploaded to BBS
Port uint32 // Port for screenshots API Port uint32 // Port for screenshots API
OutputDir string OutputDir string
UploadQuality int //Determines the upload quality to the server
} }
// DebugOptions holds various debug/temporary options for use while developing Erupe. // DebugOptions holds various debug/temporary options for use while developing Erupe.

View File

@@ -1,13 +0,0 @@
BEGIN;
CREATE TABLE public.screenshots
(
id serial PRIMARY KEY,
article_id TEXT NOT NULL,
discord_message_id TEXT,
char_id integer NOT NULL,
title TEXT NOT NULL,
description TEXT NOT NULL,
discord_img_url TEXT,
);
END;

View File

@@ -7,62 +7,47 @@ import (
"erupe-ce/network/mhfpacket" "erupe-ce/network/mhfpacket"
) )
// Handler BBS handles all the interactions with the for the screenshot sending to bulitin board functionality. For it to work it requires the API to be hosted somehwere. This implementation supports discord.
// Checks the status of the user to see if they can use Bulitin Board yet
func handleMsgMhfGetBbsUserStatus(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetBbsUserStatus(s *Session, p mhfpacket.MHFPacket) {
//Post Screenshot pauses till this succeedes //Post Screenshot pauses till this succeedes
pkt := p.(*mhfpacket.MsgMhfGetBbsUserStatus) pkt := p.(*mhfpacket.MsgMhfGetBbsUserStatus)
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
bf.WriteUint32(200) bf.WriteUint32(200) //HTTP Status Codes //200 Success //404 You wont be able to post for a certain amount of time after creating your character //401/500 A error occured server side
bf.WriteUint32(0) bf.WriteUint32(0)
bf.WriteUint32(0) bf.WriteUint32(0)
bf.WriteUint32(0) bf.WriteUint32(0)
doAckBufSucceed(s, pkt.AckHandle, bf.Data()) doAckBufSucceed(s, pkt.AckHandle, bf.Data())
} }
// Checks the status of Bultin Board Server to see if authenticated
func handleMsgMhfGetBbsSnsStatus(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetBbsSnsStatus(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetBbsSnsStatus) pkt := p.(*mhfpacket.MsgMhfGetBbsSnsStatus)
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
bf.WriteUint32(200) bf.WriteUint32(200) //200 Success //4XX Authentication has expired Please re-authenticate //5XX
bf.WriteUint32(401) bf.WriteUint32(401) //unk http status?
bf.WriteUint32(401) bf.WriteUint32(401) //unk http status?
bf.WriteUint32(0) bf.WriteUint32(0)
doAckBufSucceed(s, pkt.AckHandle, bf.Data()) doAckBufSucceed(s, pkt.AckHandle, bf.Data())
} }
// Tells the game client what host port and gives the bultin board article a token
func handleMsgMhfApplyBbsArticle(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfApplyBbsArticle(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfApplyBbsArticle) pkt := p.(*mhfpacket.MsgMhfApplyBbsArticle)
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
articleToken := token.Generate(40) articleToken := token.Generate(40)
bf.WriteUint32(200)
bf.WriteUint32(200) //http status //200 success //4XX An error occured server side
bf.WriteUint32(s.server.erupeConfig.Screenshots.Port) bf.WriteUint32(s.server.erupeConfig.Screenshots.Port)
bf.WriteUint32(0) bf.WriteUint32(0)
bf.WriteUint32(0) bf.WriteUint32(0)
bf.WriteBytes(stringsupport.PaddedString(articleToken, 64, false)) bf.WriteBytes(stringsupport.PaddedString(articleToken, 64, false))
bf.WriteBytes(stringsupport.PaddedString(s.server.erupeConfig.Screenshots.Host, 64, false)) bf.WriteBytes(stringsupport.PaddedString(s.server.erupeConfig.Screenshots.Host, 64, false))
//pkt.unk1[3] == Changes sometimes?
if s.server.erupeConfig.SaveDumps.Enabled && s.server.erupeConfig.Discord.Enabled { if s.server.erupeConfig.Screenshots.Enabled && s.server.erupeConfig.Discord.Enabled {
messageId := s.server.DiscordScreenShotSend(pkt.Name, pkt.Title, pkt.Description) // TODO: send and get back message id store in db s.server.DiscordScreenShotSend(pkt.Name, pkt.Title, pkt.Description, articleToken)
_, err := s.server.db.Exec("INSERT INTO public.screenshots (article_id,discord_message_id,char_id,title,description) VALUES ($1,$2,$3,$4,$5)", articleToken, messageId, s.charID, pkt.Title, pkt.Description)
if err != nil {
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
} else {
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
s.server.BroadcastChatMessage("Screenshot has been sent to discord")
}
} else if s.server.erupeConfig.SaveDumps.Enabled {
_, err := s.server.db.Exec("INSERT INTO public.screenshots (article_id,char_id,title,description) VALUES ($1,$2,$3,$4)", articleToken, s.charID, pkt.Title, pkt.Description)
if err != nil {
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
} else {
s.server.BroadcastChatMessage("Screenshot has been sent to server")
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
} else {
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
s.server.BroadcastChatMessage("No destination for screenshots have been configured by the host")
} }
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
} }

View File

@@ -367,13 +367,12 @@ func (s *Server) DiscordChannelSend(charName string, content string) {
} }
} }
func (s *Server) DiscordScreenShotSend(charName string, title string, description string) string { func (s *Server) DiscordScreenShotSend(charName string, title string, description string, articleToken string) {
if s.erupeConfig.Discord.Enabled && s.discordBot != nil { if s.erupeConfig.Discord.Enabled && s.discordBot != nil {
message := fmt.Sprintf("**%s**: %s - %s", charName, title, description) imageUrl := fmt.Sprintf("%s:%d/api/ss/bbs/%s", s.erupeConfig.Screenshots.Host, s.erupeConfig.Screenshots.Port, articleToken)
mesageId, _ := s.discordBot.RealtimeChannelSend(message) message := fmt.Sprintf("**%s**: %s - %s %s", charName, title, description, imageUrl)
return mesageId s.discordBot.RealtimeChannelSend(message)
} }
return ""
} }
func (s *Server) FindSessionByCharID(charID uint32) *Session { func (s *Server) FindSessionByCharID(charID uint32) *Session {

View File

@@ -1,7 +1,6 @@
package discordbot package discordbot
import ( import (
"errors"
_config "erupe-ce/config" _config "erupe-ce/config"
"regexp" "regexp"
@@ -106,16 +105,15 @@ func (bot *DiscordBot) NormalizeDiscordMessage(message string) string {
return result return result
} }
func (bot *DiscordBot) RealtimeChannelSend(message string) (messageId string, err error) { func (bot *DiscordBot) RealtimeChannelSend(message string) (err error) {
if bot.RelayChannel == nil { if bot.RelayChannel == nil {
return "", errors.New("RelayChannel is nil") return
} }
msg, err := bot.Session.ChannelMessageSend(bot.RelayChannel.ID, message) _, err = bot.Session.ChannelMessageSend(bot.RelayChannel.ID, message)
return msg.ID, err return
} }
func ReplaceTextAll(text string, regex *regexp.Regexp, handler func(input string) string) string { func ReplaceTextAll(text string, regex *regexp.Regexp, handler func(input string) string) string {
result := regex.ReplaceAllFunc([]byte(text), func(s []byte) []byte { result := regex.ReplaceAllFunc([]byte(text), func(s []byte) []byte {
input := regex.ReplaceAllString(string(s), `$1`) input := regex.ReplaceAllString(string(s), `$1`)

View File

@@ -3,18 +3,21 @@ package signv2server
import ( import (
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"encoding/xml"
"errors" "errors"
_config "erupe-ce/config" _config "erupe-ce/config"
"erupe-ce/server/channelserver" "erupe-ce/server/channelserver"
"fmt" "fmt"
"image" "image"
"image/jpeg" "image/jpeg"
"io"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
"github.com/gorilla/mux"
"github.com/lib/pq" "github.com/lib/pq"
"go.uber.org/zap" "go.uber.org/zap"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
@@ -291,35 +294,63 @@ func (s *Server) ExportSave(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json") w.Header().Add("Content-Type", "application/json")
json.NewEncoder(w).Encode(save) json.NewEncoder(w).Encode(save)
} }
func (s *Server) ScreenShotGet(w http.ResponseWriter, r *http.Request) {
func (s *Server) ScreenShot(w http.ResponseWriter, r *http.Request) { // Get the 'id' parameter from the URL
if !s.erupeConfig.SaveDumps.Enabled { vars := mux.Vars(r)
http.Error(w, "Screenshots not enabled in Config", http.StatusBadRequest) id := vars["id"]
// Open the image file
path := filepath.Join(s.erupeConfig.Screenshots.OutputDir, fmt.Sprintf("%s.jpg", id))
file, err := os.Open(path)
if err != nil {
http.Error(w, "Image not found", http.StatusNotFound)
return return
}
defer file.Close()
// Set content type header to image/jpeg
w.Header().Set("Content-Type", "image/jpeg")
// Copy the image content to the response writer
if _, err := io.Copy(w, file); err != nil {
http.Error(w, "Unable to send image", http.StatusInternalServerError)
return
}
}
func (s *Server) ScreenShot(w http.ResponseWriter, r *http.Request) {
// Create a struct representing the XML result
type Result struct {
XMLName xml.Name `xml:"result"`
Code string `xml:"code"`
}
// Set the Content-Type header to specify that the response is in XML format
w.Header().Set("Content-Type", "text/xml")
result := Result{Code: "200"}
if !s.erupeConfig.Screenshots.Enabled {
result = Result{Code: "400"}
} else { } else {
if r.Method != http.MethodPost { if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) result = Result{Code: "405"}
return
} }
// Get File from Request // Get File from Request
file, _, err := r.FormFile("img") file, _, err := r.FormFile("img")
if err != nil { if err != nil {
http.Error(w, "No valid file uploaded", http.StatusBadRequest) result = Result{Code: "400"}
return
} }
token := r.FormValue("token") token := r.FormValue("token")
if token == "" { if token == "" {
http.Error(w, "Token not specified cannot continue", http.StatusBadRequest) result = Result{Code: "400"}
return
} }
// Validate file // Validate file
img, _, err := image.Decode(file) img, _, err := image.Decode(file)
if err != nil { if err != nil {
http.Error(w, "Invalid image file", http.StatusBadRequest) result = Result{Code: "400"}
return
} }
dir := filepath.Join(s.erupeConfig.Screenshots.OutputDir) dir := filepath.Join(s.erupeConfig.Screenshots.OutputDir)
@@ -330,27 +361,37 @@ func (s *Server) ScreenShot(w http.ResponseWriter, r *http.Request) {
err = os.MkdirAll(dir, os.ModePerm) err = os.MkdirAll(dir, os.ModePerm)
if err != nil { if err != nil {
s.logger.Error("Error writing screenshot, could not create folder") s.logger.Error("Error writing screenshot, could not create folder")
return result = Result{Code: "500"}
} }
} else { } else {
s.logger.Error("Error writing screenshot") s.logger.Error("Error writing screenshot")
return result = Result{Code: "500"}
} }
} }
// Create or open the output file // Create or open the output file
outputFile, err := os.Create(path) outputFile, err := os.Create(path)
if err != nil { if err != nil {
panic(err) result = Result{Code: "500"}
} }
defer outputFile.Close() defer outputFile.Close()
// Encode the image and write it to the file // Encode the image and write it to the file
err = jpeg.Encode(outputFile, img, &jpeg.Options{}) err = jpeg.Encode(outputFile, img, &jpeg.Options{Quality: s.erupeConfig.Screenshots.UploadQuality})
if err != nil {
panic(err)
}
if err != nil { if err != nil {
s.logger.Error("Error writing screenshot, could not write file", zap.Error(err)) s.logger.Error("Error writing screenshot, could not write file", zap.Error(err))
result = Result{Code: "500"}
} }
} }
// Marshal the struct into XML
xmlData, err := xml.Marshal(result)
if err != nil {
http.Error(w, "Unable to marshal XML", http.StatusInternalServerError)
return
}
// Write the XML response with a 200 status code
w.WriteHeader(http.StatusOK)
w.Write(xmlData)
} }

View File

@@ -53,6 +53,7 @@ func (s *Server) Start() error {
r.HandleFunc("/character/delete", s.DeleteCharacter) r.HandleFunc("/character/delete", s.DeleteCharacter)
r.HandleFunc("/character/export", s.ExportSave) r.HandleFunc("/character/export", s.ExportSave)
r.HandleFunc("/api/ss/bbs/upload.php", s.ScreenShot) r.HandleFunc("/api/ss/bbs/upload.php", s.ScreenShot)
r.HandleFunc("/api/ss/bbs/{id}", s.ScreenShotGet)
handler := handlers.CORS(handlers.AllowedHeaders([]string{"Content-Type"}))(r) handler := handlers.CORS(handlers.AllowedHeaders([]string{"Content-Type"}))(r)
s.httpServer.Handler = handlers.LoggingHandler(os.Stdout, handler) s.httpServer.Handler = handlers.LoggingHandler(os.Stdout, handler)
s.httpServer.Addr = fmt.Sprintf(":%d", s.erupeConfig.SignV2.Port) s.httpServer.Addr = fmt.Sprintf(":%d", s.erupeConfig.SignV2.Port)