mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-12 23:14:36 +01:00
feat: Generate hashes for Discord and allow password resets
This commit is contained in:
20
config.json
20
config.json
@@ -19,9 +19,9 @@
|
||||
"AutoCreateAccount": true,
|
||||
"CleanDB": false,
|
||||
"MaxLauncherHR": false,
|
||||
"LogInboundMessages": false,
|
||||
"LogOutboundMessages": false,
|
||||
"LogMessageData": false,
|
||||
"LogInboundMessages": true,
|
||||
"LogOutboundMessages": true,
|
||||
"LogMessageData": true,
|
||||
"MaxHexdumpLength": 256,
|
||||
"DivaEvent": 0,
|
||||
"FestaEvent": -1,
|
||||
@@ -73,9 +73,9 @@
|
||||
"SeasonOverride": false
|
||||
},
|
||||
"Discord": {
|
||||
"Enabled": false,
|
||||
"BotToken": "",
|
||||
"RealtimeChannelID": ""
|
||||
"Enabled": true,
|
||||
"BotToken": "MTAzMTQ2MDI4MDYxOTc2NTgwMA.GGe824._OxF9rtv1O8EjOZI26hATruaF_VZ9YBwuAdS1Y",
|
||||
"RealtimeChannelID": "645108836423958540"
|
||||
},
|
||||
"Commands": [
|
||||
{
|
||||
@@ -106,6 +106,10 @@
|
||||
"Name": "PSN",
|
||||
"Enabled": true,
|
||||
"Prefix": "psn"
|
||||
}, {
|
||||
"Name": "Discord",
|
||||
"Enabled": true,
|
||||
"Prefix": "discord"
|
||||
}
|
||||
],
|
||||
"Courses": [
|
||||
@@ -125,7 +129,7 @@
|
||||
"Host": "localhost",
|
||||
"Port": 5432,
|
||||
"User": "postgres",
|
||||
"Password": "",
|
||||
"Password": "admin",
|
||||
"Database": "erupe"
|
||||
},
|
||||
"Sign": {
|
||||
@@ -133,7 +137,7 @@
|
||||
"Port": 53312
|
||||
},
|
||||
"SignV2": {
|
||||
"Enabled": false,
|
||||
"Enabled": true,
|
||||
"Port": 8080,
|
||||
"PatchServer": "",
|
||||
"Banners": [],
|
||||
|
||||
32
main.go
32
main.go
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
_config "erupe-ce/config"
|
||||
"fmt"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
@@ -98,6 +99,37 @@ func main() {
|
||||
}
|
||||
|
||||
discordBot = bot
|
||||
|
||||
_, err = discordBot.Session.ApplicationCommandBulkOverwrite(discordBot.Session.State.User.ID, "", []*discordgo.ApplicationCommand{
|
||||
{
|
||||
Name: "verify",
|
||||
Description: "Verify your account with Discord",
|
||||
Options: []*discordgo.ApplicationCommandOption{
|
||||
{
|
||||
Type: discordgo.ApplicationCommandOptionString,
|
||||
Name: "token",
|
||||
Description: "The access token provided by !discord command within the game client.",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "passwordreset",
|
||||
Description: "Reset your account password on Erupe",
|
||||
Options: []*discordgo.ApplicationCommandOption{
|
||||
{
|
||||
Type: discordgo.ApplicationCommandOptionString,
|
||||
Name: "password",
|
||||
Description: "The password to change your account to.",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
preventClose(fmt.Sprintf("Discord: Failed to start, %s", err.Error()))
|
||||
}
|
||||
|
||||
logger.Info("Discord: Started successfully")
|
||||
} else {
|
||||
logger.Info("Discord: Disabled")
|
||||
|
||||
@@ -2,6 +2,7 @@ package channelserver
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/common/mhfcourse"
|
||||
@@ -321,12 +322,22 @@ func parseChatCommand(s *Session, command string) {
|
||||
}
|
||||
case commands["Discord"].Prefix:
|
||||
if commands["Discord"].Enabled {
|
||||
token := crypto.MD5.New()
|
||||
_, err := s.server.db.Exec("UPDATE users SET discord_token = ?", token)
|
||||
tokenHash := crypto.MD5.New()
|
||||
tokenSalt := fmt.Sprint(s.charID) + fmt.Sprint(s.server.ID)
|
||||
tokenData := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(tokenData, uint32(time.Now().Second()))
|
||||
tokenHash.Write([]byte(fmt.Sprintf("%s%s", tokenSalt, tokenData)))
|
||||
discordToken := fmt.Sprint(tokenHash)[4:12]
|
||||
s.logger.Info(discordToken)
|
||||
_, err := s.server.db.Exec("UPDATE users u SET discord_token = $1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", discordToken, s.charID)
|
||||
if err != nil {
|
||||
sendServerChatMessage(s, fmt.Sprint("An error occurred while processing this command"))
|
||||
s.logger.Error(fmt.Sprint(err))
|
||||
return
|
||||
}
|
||||
sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandDiscord"], token))
|
||||
sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandDiscordSuccess"], discordToken))
|
||||
} else {
|
||||
sendDisabledCommandMessage(s, commands["Discord"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package channelserver
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
@@ -70,14 +71,32 @@ func getCharacterList(s *Server) string {
|
||||
func (s *Server) onInteraction(ds *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
switch i.Interaction.ApplicationCommandData().Name {
|
||||
case "verify":
|
||||
_, err := s.db.Exec("UPDATE users SET discord_id = ? WHERE discord_token = ?", i.User.ID, i.Interaction.ApplicationCommandData().Options[0].StringValue())
|
||||
_, err := s.db.Exec("UPDATE users SET discord_id = $1 WHERE discord_token = $2", i.Member.User.ID, i.ApplicationCommandData().Options[0].StringValue())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = ds.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: "Account successfully linked",
|
||||
Content: "Erupe account successfully linked to Discord account.",
|
||||
Flags: discordgo.MessageFlagsEphemeral,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
break
|
||||
case "passwordreset":
|
||||
password, _ := bcrypt.GenerateFromPassword([]byte(i.ApplicationCommandData().Options[0].StringValue()), 10)
|
||||
_, err := s.db.Exec("UPDATE users SET password = $1 WHERE discord_id = $2", password, i.Member.User.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = ds.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: "Password has been reset, you may login now.",
|
||||
Flags: discordgo.MessageFlagsEphemeral,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -55,6 +55,7 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) {
|
||||
|
||||
// Save our new stage ID and pointer to the new stage itself.
|
||||
s.Lock()
|
||||
s.stageID = stageID
|
||||
s.stage = s.server.stages[stageID]
|
||||
s.Unlock()
|
||||
|
||||
@@ -152,13 +153,13 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgSysEnterStage)
|
||||
|
||||
// Push our current stage ID to the movement stack before entering another one.
|
||||
if s.stage.id == "" {
|
||||
if s.stageID == "" {
|
||||
s.stageMoveStack.Set(pkt.StageID)
|
||||
} else {
|
||||
s.stage.Lock()
|
||||
s.stage.reservedClientSlots[s.charID] = false
|
||||
s.stage.Unlock()
|
||||
s.stageMoveStack.Push(s.stage.id)
|
||||
s.stageMoveStack.Push(s.stageID)
|
||||
s.stageMoveStack.Lock()
|
||||
}
|
||||
|
||||
@@ -205,12 +206,9 @@ func handleMsgSysLeaveStage(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgSysLockStage(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgSysLockStage)
|
||||
if stage, exists := s.server.stages[pkt.StageID]; exists {
|
||||
stage.Lock()
|
||||
stage.locked = true
|
||||
stage.Unlock()
|
||||
}
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
// TODO(Andoryuuta): What does this packet _actually_ do?
|
||||
// I think this is supposed to mark a stage as no longer able to accept client reservations
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
}
|
||||
|
||||
func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) {
|
||||
@@ -220,9 +218,7 @@ func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
for charID := range s.reservationStage.reservedClientSlots {
|
||||
session := s.server.FindSessionByCharID(charID)
|
||||
if session != nil {
|
||||
session.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{})
|
||||
}
|
||||
session.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{})
|
||||
}
|
||||
|
||||
delete(s.server.stages, s.reservationStage.id)
|
||||
@@ -245,10 +241,6 @@ func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
} else if uint16(len(stage.reservedClientSlots)) < stage.maxPlayers {
|
||||
if stage.locked {
|
||||
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
}
|
||||
if len(stage.password) > 0 {
|
||||
if stage.password != s.stagePass {
|
||||
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||
@@ -391,17 +383,20 @@ func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) {
|
||||
joinable++
|
||||
|
||||
bf.WriteUint16(uint16(len(stage.reservedClientSlots)))
|
||||
bf.WriteUint16(uint16(len(stage.clients)))
|
||||
bf.WriteUint16(uint16(len(stage.clients)))
|
||||
bf.WriteUint16(0) // Unk
|
||||
if len(stage.clients) > 0 {
|
||||
bf.WriteUint16(1)
|
||||
} else {
|
||||
bf.WriteUint16(0)
|
||||
}
|
||||
bf.WriteUint16(stage.maxPlayers)
|
||||
var flags uint8
|
||||
if stage.locked {
|
||||
flags |= 1
|
||||
}
|
||||
if len(stage.password) > 0 {
|
||||
flags |= 2
|
||||
// This byte has also been seen as 1
|
||||
// The quest is also recognised as locked when this is 2
|
||||
bf.WriteUint8(2)
|
||||
} else {
|
||||
bf.WriteUint8(0)
|
||||
}
|
||||
bf.WriteUint8(flags)
|
||||
ps.Uint8(bf, sid, false)
|
||||
stage.RUnlock()
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package channelserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -211,15 +210,6 @@ func (s *Server) Start() error {
|
||||
|
||||
// Start the discord bot for chat integration.
|
||||
if s.erupeConfig.Discord.Enabled && s.discordBot != nil {
|
||||
_, err := s.discordBot.Session.ApplicationCommandBulkOverwrite(s.discordBot.Session.State.User.ID, "", []*discordgo.ApplicationCommand{
|
||||
{
|
||||
Name: "verify",
|
||||
Description: "Verify your account with Discord",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.discordBot.Session.AddHandler(s.onDiscordMessage)
|
||||
s.discordBot.Session.AddHandler(s.onInteraction)
|
||||
}
|
||||
|
||||
@@ -76,6 +76,8 @@ func getLangStrings(s *Server) map[string]string {
|
||||
strings["commandPSNSuccess"] = "Connected PSN ID: %s"
|
||||
strings["commandPSNExists"] = "PSN ID is connected to another account!"
|
||||
|
||||
strings["commandDiscordSuccess"] = "Discord token has been generated: %s"
|
||||
|
||||
strings["commandRaviNoCommand"] = "No Raviente command specified!"
|
||||
strings["commandRaviStartSuccess"] = "The Great Slaying will begin in a moment"
|
||||
strings["commandRaviStartError"] = "The Great Slaying has already begun!"
|
||||
|
||||
@@ -36,6 +36,7 @@ type Session struct {
|
||||
|
||||
objectIndex uint16
|
||||
userEnteredStage bool // If the user has entered a stage before
|
||||
stageID string
|
||||
stage *Stage
|
||||
reservationStage *Stage // Required for the stateful MsgSysUnreserveStage packet.
|
||||
stagePass string // Temporary storage
|
||||
|
||||
Reference in New Issue
Block a user