Skip to content

Commit 74724f9

Browse files
authored
Merge pull request #13 from Retro-Rewind-Team/discord-link-rebase
2 parents 66288fd + c14594e commit 74724f9

File tree

12 files changed

+173
-9
lines changed

12 files changed

+173
-9
lines changed

api/link.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package api
2+
3+
import (
4+
"errors"
5+
"net/http"
6+
"wwfc/database"
7+
"wwfc/gpcm"
8+
)
9+
10+
11+
type LinkRequest struct {
12+
Secret string `json:"secret"`
13+
ProfileID uint32 `json:"pid"`
14+
DiscordID string `json:"discordId"`
15+
Action string `json:"action"`
16+
}
17+
18+
var LinkRoute = MakeRouteSpec[LinkRequest, UserActionResponse](
19+
true,
20+
"/api/link",
21+
func(req any, v bool, _ *http.Request) (any, int, error) {
22+
return handleUserAction(req.(LinkRequest), v, HandleLink)
23+
},
24+
http.MethodPost,
25+
)
26+
27+
var (
28+
ErrPIDIsMissing = errors.New("Profile ID missing or invalid")
29+
ErrDiscordIDMissing = errors.New("Discord ID missing or invalid")
30+
ErrDiscordLinked = errors.New("Discord ID already linked to another profile")
31+
ErrDiscordWrongStep = errors.New("Profile is not in the correct step to link Discord ID")
32+
ErrDiscordCannotUnlink = errors.New("Discord ID cannot be unlinked")
33+
ErrActionMissing = errors.New("Action missing in request")
34+
)
35+
36+
func HandleLink(req LinkRequest, _ bool) (*database.User, int, error) {
37+
var err error
38+
39+
if req.ProfileID == 0 {
40+
return nil, http.StatusBadRequest, ErrPIDIsMissing
41+
}
42+
43+
if req.DiscordID == "" {
44+
return nil, http.StatusBadRequest, ErrDiscordIDMissing
45+
}
46+
47+
if req.Action == "" || (req.Action != "link" && req.Action != "check" && req.Action != "unlink") {
48+
return nil, http.StatusBadRequest, ErrActionMissing
49+
}
50+
51+
user, success := database.GetProfile(pool, ctx, req.ProfileID)
52+
if success != nil {
53+
return nil, http.StatusInternalServerError, ErrUserQuery
54+
}
55+
56+
if req.Action == "link" {
57+
if user.DiscordID != "" {
58+
return nil, http.StatusForbidden, ErrDiscordLinked
59+
}
60+
gpcm.SetSessionDiscordID(user.ProfileId, "1")
61+
err = user.UpdateDiscordID(pool, ctx, "1")
62+
if err != nil {
63+
return nil, http.StatusInternalServerError, err
64+
}
65+
} else if req.Action == "check" {
66+
if user.DiscordID != "2" {
67+
return nil, http.StatusForbidden, ErrDiscordWrongStep
68+
}
69+
gpcm.SetSessionDiscordID(user.ProfileId, req.DiscordID)
70+
err = user.UpdateDiscordID(pool, ctx, req.DiscordID)
71+
if err != nil {
72+
return nil, http.StatusInternalServerError, err
73+
}
74+
} else if req.Action == "unlink" {
75+
if user.DiscordID != "1" && user.DiscordID != "2" && user.DiscordID != req.DiscordID {
76+
return nil, http.StatusForbidden, ErrDiscordCannotUnlink
77+
}
78+
err = user.UpdateDiscordID(pool, ctx, "")
79+
if err != nil {
80+
return nil, http.StatusInternalServerError, err
81+
}
82+
}
83+
84+
return &user, http.StatusOK, nil
85+
}

api/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ func StartServer(reload bool) {
9797
ClearRoute,
9898
GetHashRoute,
9999
KickRoute,
100+
LinkRoute,
100101
MotdRoute,
101102
PinfoRoute,
102103
QueryRoute,

api/query.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type QueryRequest struct {
1313
IP string `json:"ip"`
1414
DeviceID uint32 `json:"deviceID"`
1515
Csnum string `json:"csnum"`
16+
DiscordID string `json:"discordId"`
1617
// 0: Either, 1: No Ban, 2: Ban
1718
HasBan byte `json:"hasban"`
1819
}
@@ -34,9 +35,9 @@ var (
3435
ErrInvalidIPFormat = errors.New("Invalid IP Format. IPs must be in the format 'xx.xx.xx.xx'.")
3536
ErrInvalidDeviceID = errors.New("DeviceID cannot be 0.")
3637
ErrInvalidCsnum = errors.New("Csnums must be less than 16 characters long and match the format '^[a-zA-Z0-9]+$'.")
38+
ErrInvalidDiscordID = errors.New("Discord ID must be less than 18 characters long.")
3739
ErrInvalidHasBan = errors.New("HasBan must be either 0 (Either), 1 (No Ban), 2 (Ban)")
3840
ErrEmptyParams = errors.New("At least one of IP, Csnum, and DeviceID must be nonzero or nonempty")
39-
4041
ipRegex = regexp.MustCompile(`^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$`)
4142
csnumRegex = regexp.MustCompile(`^[a-zA-Z0-9]+$`)
4243
)
@@ -54,11 +55,15 @@ func HandleQuery(req any, _ bool, _ *http.Request) (any, int, error) {
5455
return nil, http.StatusBadRequest, ErrInvalidCsnum
5556
}
5657

58+
if _req.DiscordID != "" && len(_req.DiscordID) > 18 {
59+
return nil, http.StatusBadRequest, ErrInvalidDiscordID
60+
}
61+
5762
if _req.HasBan != 0 && _req.HasBan != 1 && _req.HasBan != 2 {
5863
return nil, http.StatusBadRequest, ErrInvalidHasBan
5964
}
6065

61-
if _req.IP == "" && _req.Csnum == "" && _req.DeviceID == 0 {
66+
if _req.IP == "" && _req.Csnum == "" && _req.DeviceID == 0 && _req.DiscordID == "" {
6267
return nil, http.StatusBadRequest, ErrEmptyParams
6368
}
6469

@@ -76,6 +81,10 @@ func HandleQuery(req any, _ bool, _ *http.Request) (any, int, error) {
7681
query += fmt.Sprintf(" '%s' = ANY(csnum) AND", _req.Csnum)
7782
}
7883

84+
if _req.DiscordID != "" {
85+
query += fmt.Sprintf(" discord_id = '%s' AND", _req.DiscordID)
86+
}
87+
7988
if _req.HasBan == 1 {
8089
query += fmt.Sprintf(" has_ban = false AND")
8190
} else if _req.HasBan == 2 {

common/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ type Config struct {
4040
WiiCertPathDS string `xml:"wiiCertDerPathDS"`
4141
KeyPathDS string `xml:"keyPathDS"`
4242

43+
FriendBotPID string `xml:"friendBotPID"`
4344
APISecret string `xml:"apiSecret"`
4445

4546
AllowDefaultDolphinKeys bool `xml:"allowDefaultDolphinKeys"`

config_example.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@
8181
-->
8282
<logOutput>StdOutAndFile</logOutput>
8383

84+
<!-- FriendBot PID -->
85+
<friendBotPID>1234</friendBotPID>
86+
8487
<!-- API secret -->
8588
<apiSecret>hQ3f57b3tW2WnjJH3v</apiSecret>
8689
</Config>

database/login.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,9 @@ func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsb
109109
} else {
110110
var firstName *string
111111
var lastName *string
112+
var discordId *string
112113

113-
err := pool.QueryRow(ctx, GetUserProfileID, userId, gsbrcd).Scan(&user.ProfileId, &user.NgDeviceId, &user.Email, &user.UniqueNick, &firstName, &lastName, &user.OpenHost, &lastIPAddress, &user.Csnum)
114+
err := pool.QueryRow(ctx, GetUserProfileID, userId, gsbrcd).Scan(&user.ProfileId, &user.NgDeviceId, &user.Email, &user.UniqueNick, &firstName, &lastName, &user.OpenHost, &discordId, &lastIPAddress, &user.Csnum)
114115
if err != nil {
115116
return User{}, err
116117
}
@@ -123,6 +124,10 @@ func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsb
123124
user.LastName = *lastName
124125
}
125126

127+
if discordId != nil {
128+
user.DiscordID = *discordId
129+
}
130+
126131
validDeviceId := false
127132
deviceIdList := ""
128133
for index, id := range user.NgDeviceId {
@@ -269,7 +274,8 @@ func LoginUserToGameStats(pool *pgxpool.Pool, ctx context.Context, userId uint64
269274
var firstName *string
270275
var lastName *string
271276
var lastIPAddress *string
272-
err := pool.QueryRow(ctx, GetUserProfileID, userId, gsbrcd).Scan(&user.ProfileId, &user.NgDeviceId, &user.Email, &user.UniqueNick, &firstName, &lastName, &user.OpenHost, &lastIPAddress, &user.Csnum)
277+
var discordId *string
278+
err := pool.QueryRow(ctx, GetUserProfileID, userId, gsbrcd).Scan(&user.ProfileId, &user.NgDeviceId, &user.Email, &user.UniqueNick, &firstName, &lastName, &user.OpenHost, &discordId, &lastIPAddress, &user.Csnum)
273279
if err != nil {
274280
return User{}, err
275281
}
@@ -282,5 +288,9 @@ func LoginUserToGameStats(pool *pgxpool.Pool, ctx context.Context, userId uint64
282288
user.LastName = *lastName
283289
}
284290

291+
if discordId != nil {
292+
user.DiscordID = *discordId
293+
}
294+
285295
return user, nil
286296
}

database/schema.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ func UpdateTables(pool *pgxpool.Pool, ctx context.Context) {
2020
ADD IF NOT EXISTS ban_moderator character varying,
2121
ADD IF NOT EXISTS ban_tos boolean,
2222
ADD IF NOT EXISTS open_host boolean DEFAULT false;
23+
ADD IF NOT EXISTS discord_id character varying
2324
2425
`)
2526

database/user.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@ const (
1616
UpdateUserProfileID = `UPDATE users SET profile_id = $3 WHERE user_id = $1 AND gsbrcd = $2`
1717
UpdateUserNGDeviceID = `UPDATE users SET ng_device_id = $2 WHERE profile_id = $1`
1818
UpdateUserCsnum = `UPDATE users SET csnum = $2 WHERE profile_id = $1`
19-
GetUser = `SELECT user_id, gsbrcd, ng_device_id, email, unique_nick, firstname, lastname, has_ban, ban_reason, open_host, last_ingamesn, last_ip_address, csnum, ban_moderator, ban_reason_hidden, ban_issued, ban_expires FROM users WHERE profile_id = $1`
19+
GetUser = `SELECT user_id, gsbrcd, ng_device_id, email, unique_nick, firstname, lastname, has_ban, ban_reason, open_host, last_ingamesn, last_ip_address, csnum, discord_id, ban_moderator, ban_reason_hidden, ban_issued, ban_expires FROM users WHERE profile_id = $1`
2020
ClearProfileQuery = `DELETE FROM users WHERE profile_id = $1 RETURNING user_id, gsbrcd, email, unique_nick, firstname, lastname, open_host, last_ip_address, last_ingamesn, csnum`
2121
DoesUserExist = `SELECT EXISTS(SELECT 1 FROM users WHERE user_id = $1 AND gsbrcd = $2)`
2222
IsProfileIDInUse = `SELECT EXISTS(SELECT 1 FROM users WHERE profile_id = $1)`
2323
DeleteUserSession = `DELETE FROM sessions WHERE profile_id = $1`
24-
GetUserProfileID = `SELECT profile_id, ng_device_id, email, unique_nick, firstname, lastname, open_host, last_ip_address, csnum FROM users WHERE user_id = $1 AND gsbrcd = $2`
24+
GetUserProfileID = `SELECT profile_id, ng_device_id, email, unique_nick, firstname, lastname, open_host, discord_id, last_ip_address, csnum FROM users WHERE user_id = $1 AND gsbrcd = $2`
2525
UpdateUserLastIPAddress = `UPDATE users SET last_ip_address = $2, last_ingamesn = $3 WHERE profile_id = $1`
26+
UpdateDiscordID = `UPDATE users SET discord_id = $2 WHERE profile_id = $1`
2627
UpdateUserBan = `UPDATE users SET has_ban = true, ban_issued = $2, ban_expires = $3, ban_reason = $4, ban_reason_hidden = $5, ban_moderator = $6, ban_tos = $7 WHERE profile_id = $1`
2728
DisableUserBan = `UPDATE users SET has_ban = false WHERE profile_id = $1`
2829

@@ -46,6 +47,7 @@ type User struct {
4647
LastInGameSn string
4748
LastIPAddress string
4849
Csnum []string
50+
DiscordID string
4951
// Following fields only used in GetUser query
5052
BanModerator string
5153
BanReasonHidden string
@@ -104,6 +106,14 @@ func (user *User) UpdateProfileID(pool *pgxpool.Pool, ctx context.Context, newPr
104106
return err
105107
}
106108

109+
func (user *User) UpdateDiscordID(pool *pgxpool.Pool, ctx context.Context, discordId string) error {
110+
_, err := pool.Exec(ctx, UpdateDiscordID, user.ProfileId, discordId)
111+
if err == nil {
112+
user.DiscordID = discordId
113+
}
114+
return err
115+
}
116+
107117
func GetUniqueUserID() uint64 {
108118
// Not guaranteed unique but doesn't matter in practice if multiple people have the same user ID.
109119
return uint64(rand.Int63n(0x80000000000))
@@ -148,8 +158,9 @@ func GetProfile(pool *pgxpool.Pool, ctx context.Context, profileId uint32) (User
148158
var lastIPAddress *string
149159
var banModerator *string
150160
var banHiddenReason *string
161+
var discordID *string
151162

152-
err := row.Scan(&user.UserId, &user.GsbrCode, &user.NgDeviceId, &user.Email, &user.UniqueNick, &firstName, &lastName, &user.Restricted, &banReason, &user.OpenHost, &lastInGameSn, &lastIPAddress, &user.Csnum, &banModerator, &banHiddenReason, &user.BanIssued, &user.BanExpires)
163+
err := row.Scan(&user.UserId, &user.GsbrCode, &user.NgDeviceId, &user.Email, &user.UniqueNick, &firstName, &lastName, &user.Restricted, &banReason, &user.OpenHost, &lastInGameSn, &lastIPAddress, &user.Csnum, &discordID, &banModerator, &banHiddenReason, &user.BanIssued, &user.BanExpires)
153164

154165
if err != nil {
155166
return User{}, err
@@ -185,6 +196,10 @@ func GetProfile(pool *pgxpool.Pool, ctx context.Context, profileId uint32) (User
185196
user.BanReasonHidden = *banHiddenReason
186197
}
187198

199+
if discordID != nil {
200+
user.DiscordID = *discordID
201+
}
202+
188203
return user, nil
189204
}
190205

gpcm/friend.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,23 @@ func (g *GameSpySession) getAuthorizedFriendIndex(profileId uint32) int {
6262
return -1
6363
}
6464

65+
func (g *GameSpySession) handleFriendBot() {
66+
67+
if g.User.DiscordID != "1" {
68+
logging.Warn(g.ModuleName, "FriendBot is added but verification is not initialized, currently", g.User.DiscordID)
69+
return
70+
}
71+
72+
logging.Info(g.ModuleName, "FriendBot verification progressed to step 2")
73+
SetSessionDiscordID(g.User.ProfileId, "2")
74+
err := g.User.UpdateDiscordID(pool, ctx, "2")
75+
if err != nil {
76+
logging.Error(g.ModuleName, "Failed to update discord ID:", err)
77+
return
78+
79+
}
80+
}
81+
6582
const (
6683
// addFriendMessage = "\r\n\r\n|signed|00000000000000000000000000000000"
6784

@@ -77,6 +94,11 @@ func (g *GameSpySession) isBm1AuthMessageNeeded() bool {
7794

7895
func (g *GameSpySession) addFriend(command common.GameSpyCommand) {
7996
strNewProfileId := command.OtherValues["newprofileid"]
97+
98+
if strNewProfileId == config.FriendBotPID {
99+
logging.Info(g.ModuleName, "Attempt to add FriendBot as a friend")
100+
g.handleFriendBot()
101+
}
80102
newProfileId, err := strconv.ParseUint(strNewProfileId, 10, 32)
81103
if err != nil {
82104
g.replyError(ErrAddFriend)

gpcm/main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,15 @@ var (
6868
mutex = deadlock.Mutex{}
6969

7070
allowDefaultDolphinKeys bool
71+
config common.Config
7172
)
7273

74+
7375
func StartServer(reload bool) {
7476
qr2.SetGPErrorCallback(KickPlayer)
7577

7678
// Get config
77-
config := common.GetConfig()
79+
config = common.GetConfig()
7880

7981
// Start SQL
8082
dbString := fmt.Sprintf("postgres://%s:%s@%s/%s", config.Username, config.Password, config.DatabaseAddress, config.DatabaseName)

0 commit comments

Comments
 (0)