2024-07-06 16:18:05 +02:00
package main
import (
2024-07-06 21:54:33 +02:00
"bytes"
2024-07-06 17:27:04 +02:00
"database/sql"
2024-07-06 21:54:33 +02:00
"encoding/json"
2024-07-06 16:18:05 +02:00
"fmt"
2024-07-06 17:16:13 +02:00
"log"
2024-07-06 21:54:33 +02:00
"mime/multipart"
"net/http"
2024-07-06 16:55:30 +02:00
"os"
"os/signal"
2024-07-06 21:54:33 +02:00
"strconv"
2024-07-06 19:22:38 +02:00
"strings"
2024-07-06 18:06:55 +02:00
"syscall"
2024-07-06 21:54:33 +02:00
"time"
2024-07-06 16:55:30 +02:00
2024-07-06 16:29:25 +02:00
"github.com/BurntSushi/toml"
"github.com/bwmarrin/discordgo"
2024-07-06 21:54:33 +02:00
"github.com/lib/pq"
2024-07-06 16:18:05 +02:00
)
2024-07-06 21:54:33 +02:00
type UserResponse struct {
ID string ` json:"id" `
Username string ` json:"username" `
Subscription bool ` json:"subscription" `
Group string ` json:"group" `
Expiration string ` json:"expiration" `
Versions [ ] string ` json:"versions" `
PFP string ` json:"pfp" `
Message int ` json:"message" `
}
2024-07-06 17:16:13 +02:00
var (
2024-07-06 17:27:04 +02:00
config Config
client * discordgo . Session
db * sql . DB
2024-07-06 17:16:13 +02:00
)
2024-07-06 16:55:30 +02:00
2024-07-06 16:29:25 +02:00
type Config struct {
2024-07-06 21:01:44 +02:00
Discord Discord ` toml:"discord" `
Database Database ` toml:"database" `
2024-07-06 16:29:25 +02:00
}
2024-07-06 16:59:12 +02:00
type Discord struct {
2024-07-06 21:01:44 +02:00
Token string ` toml:"token" `
AppID string ` toml:"appid" `
GuildID string ` toml:"guildid" `
CategoryID string ` toml:"category_id" `
AdminRoles [ ] string ` toml:"admin_roles" `
2024-07-06 16:59:12 +02:00
}
type Database struct {
Host string ` toml:"host" `
Port int ` toml:"port" `
Name string ` toml:"name" `
Username string ` toml:"username" `
Password string ` toml:"password" `
}
2024-07-06 16:29:25 +02:00
func loadConfig ( filename string ) ( Config , error ) {
var config Config
_ , err := toml . DecodeFile ( filename , & config )
return config , err
}
2024-07-06 17:27:04 +02:00
func connectDb ( config Config ) ( * sql . DB , error ) {
2024-07-06 17:16:13 +02:00
connectionString := fmt . Sprintf (
"host=%s port=%d user=%s password=%s dbname=%s sslmode=disable" ,
config . Database . Host ,
config . Database . Port ,
config . Database . Username ,
config . Database . Password ,
config . Database . Name ,
)
db , err := sql . Open ( "postgres" , connectionString )
if err != nil {
2024-07-06 17:27:04 +02:00
return nil , fmt . Errorf ( "error connecting to the database: %v" , err )
2024-07-06 17:16:13 +02:00
}
err = db . Ping ( )
if err != nil {
2024-07-06 17:27:04 +02:00
db . Close ( )
return nil , fmt . Errorf ( "error pinging the database: %v" , err )
2024-07-06 17:16:13 +02:00
}
2024-07-06 17:27:04 +02:00
log . Println ( "Successfully connected to the database." )
return db , nil
2024-07-06 17:16:13 +02:00
}
2024-07-06 16:55:30 +02:00
func init ( ) {
2024-07-06 17:16:13 +02:00
var err error
config , err = loadConfig ( "config.toml" )
2024-07-06 16:29:25 +02:00
if err != nil {
2024-07-06 17:16:13 +02:00
log . Println ( "Error occurred whilst trying to load config:" , err )
2024-07-06 16:29:25 +02:00
return
}
2024-07-06 16:55:30 +02:00
2024-07-06 16:59:12 +02:00
client , err = discordgo . New ( "Bot " + config . Discord . Token )
2024-07-06 16:29:25 +02:00
if err != nil {
2024-07-06 17:16:13 +02:00
log . Println ( "Error initializing bot:" , err )
2024-07-06 16:29:25 +02:00
return
}
2024-07-06 16:55:30 +02:00
2024-07-06 17:27:04 +02:00
db , err = connectDb ( config )
if err != nil {
log . Println ( "Error initializing db connection:" , err )
return
}
2024-07-06 16:18:05 +02:00
}
2024-07-06 16:55:30 +02:00
2024-07-06 19:22:38 +02:00
func reset ( userNickname , softwareType string ) {
log . Printf ( "Resetting %s for user %s\n" , softwareType , userNickname )
}
2024-07-06 20:37:14 +02:00
func getUsernameFromMember ( member * discordgo . Member ) string {
if member == nil {
return "UnknownUser"
}
var userName string
if member . Nick != "" {
userName = member . Nick
2024-07-06 22:56:19 +02:00
} else if member . User . GlobalName != "" {
userName = member . User . GlobalName
2024-07-06 20:37:14 +02:00
} else {
2024-07-06 22:56:19 +02:00
userName = member . User . Username
2024-07-06 20:37:14 +02:00
}
return userName
}
2024-07-06 17:37:07 +02:00
var (
commands = [ ] discordgo . ApplicationCommand {
{
2024-07-06 18:57:43 +02:00
Name : "setup" ,
Description : "Setup a channel for ticket creation" ,
Options : [ ] * discordgo . ApplicationCommandOption {
{
Type : discordgo . ApplicationCommandOptionChannel ,
Name : "channel" ,
Description : "Channel for ticket creation" ,
Required : true ,
} ,
} ,
2024-07-06 17:37:07 +02:00
} ,
2024-07-18 14:12:21 +02:00
{
2024-07-18 14:28:20 +02:00
Name : "resetvanity" ,
Description : "Reset a user's HWID for Vanity by username or UID" ,
2024-07-18 14:12:21 +02:00
Options : [ ] * discordgo . ApplicationCommandOption {
{
Type : discordgo . ApplicationCommandOptionString ,
Name : "identifier" ,
2024-07-18 14:48:16 +02:00
Description : "Usernames or UIDs, separated by commas" ,
2024-07-18 14:12:21 +02:00
Required : true ,
} ,
2024-07-18 14:28:20 +02:00
} ,
} ,
{
Name : "resetmesa" ,
Description : "Reset a user's HWID for Mesa by username or UID" ,
Options : [ ] * discordgo . ApplicationCommandOption {
2024-07-18 14:12:21 +02:00
{
Type : discordgo . ApplicationCommandOptionString ,
2024-07-18 14:28:20 +02:00
Name : "identifier" ,
2024-07-18 14:48:16 +02:00
Description : "Usernames or UIDs, separated by commas" ,
2024-07-18 14:12:21 +02:00
Required : true ,
} ,
} ,
} ,
2024-07-06 17:37:07 +02:00
}
2024-07-06 17:52:22 +02:00
commandsHandlers = map [ string ] func ( client * discordgo . Session , i * discordgo . InteractionCreate ) {
2024-07-06 18:57:43 +02:00
"setup" : func ( client * discordgo . Session , i * discordgo . InteractionCreate ) {
2024-07-06 22:02:03 +02:00
hasAdminPermission := false
for _ , roleID := range config . Discord . AdminRoles {
member , err := client . GuildMember ( i . GuildID , i . Member . User . ID )
if err != nil {
log . Println ( "Error fetching member info:" , err )
return
}
for _ , role := range member . Roles {
if role == roleID {
hasAdminPermission = true
break
}
}
if hasAdminPermission {
break
}
}
if ! hasAdminPermission {
err := client . InteractionRespond ( i . Interaction , & discordgo . InteractionResponse {
Type : discordgo . InteractionResponseChannelMessageWithSource ,
Data : & discordgo . InteractionResponseData {
Content : "You do not have permission to run this command." ,
Flags : discordgo . MessageFlagsEphemeral ,
} ,
} )
if err != nil {
log . Println ( "Error sending interaction response:" , err )
}
return
}
2024-07-06 18:57:43 +02:00
channelOption := i . ApplicationCommandData ( ) . Options [ 0 ] . ChannelValue ( client )
_ , err := client . ChannelMessageSendComplex ( channelOption . ID , & discordgo . MessageSend {
Content : "Click the button below to request a HWID reset:" ,
Components : [ ] discordgo . MessageComponent {
discordgo . ActionsRow {
Components : [ ] discordgo . MessageComponent {
discordgo . Button {
Label : "Request Reset" ,
Style : discordgo . PrimaryButton ,
CustomID : "create_ticket" ,
} ,
} ,
} ,
} ,
} )
if err != nil {
log . Println ( "Error sending message to the specified channel:" , err )
}
err = client . InteractionRespond ( i . Interaction , & discordgo . InteractionResponse {
Type : discordgo . InteractionResponseChannelMessageWithSource ,
Data : & discordgo . InteractionResponseData {
Content : fmt . Sprintf ( "Setup completed! Request creation button has been added to %s" , channelOption . Mention ( ) ) ,
} ,
} )
if err != nil {
log . Println ( "Error sending interaction response:" , err )
}
} ,
2024-07-18 14:28:20 +02:00
"resetvanity" : func ( client * discordgo . Session , i * discordgo . InteractionCreate ) {
resetCommandHandler ( client , i , "vanity" )
} ,
"resetmesa" : func ( client * discordgo . Session , i * discordgo . InteractionCreate ) {
resetCommandHandler ( client , i , "mesa" )
2024-07-18 14:12:21 +02:00
} ,
2024-07-06 18:57:43 +02:00
}
2024-07-06 22:02:03 +02:00
2024-07-06 18:57:43 +02:00
componentsHandlers = map [ string ] func ( client * discordgo . Session , i * discordgo . InteractionCreate ) {
"create_ticket" : func ( client * discordgo . Session , i * discordgo . InteractionCreate ) {
2024-07-06 17:52:22 +02:00
err := client . InteractionRespond ( i . Interaction , & discordgo . InteractionResponse {
2024-07-06 17:37:07 +02:00
Type : discordgo . InteractionResponseChannelMessageWithSource ,
Data : & discordgo . InteractionResponseData {
2024-07-06 19:22:38 +02:00
Content : "For what would you like a HWID reset:" ,
2024-07-06 18:57:43 +02:00
Components : [ ] discordgo . MessageComponent {
discordgo . ActionsRow {
Components : [ ] discordgo . MessageComponent {
discordgo . SelectMenu {
CustomID : "select_software_type" ,
Placeholder : "Choose which software" ,
Options : [ ] discordgo . SelectMenuOption {
{
Label : "Vanity" ,
Value : "vanity" ,
Description : "Request a vanity reset" ,
} ,
{
Label : "Mesa" ,
Value : "mesa" ,
Description : "Request a mesa reset" ,
} ,
} ,
} ,
} ,
} ,
} ,
2024-07-06 20:07:07 +02:00
Flags : discordgo . MessageFlagsEphemeral ,
2024-07-06 17:37:07 +02:00
} ,
} )
if err != nil {
2024-07-06 18:57:43 +02:00
log . Println ( "Error sending interaction response:" , err )
}
} ,
"select_software_type" : func ( client * discordgo . Session , i * discordgo . InteractionCreate ) {
selectedOption := i . MessageComponentData ( ) . Values [ 0 ]
var softwareType string
if selectedOption == "vanity" {
softwareType = "Vanity"
} else if selectedOption == "mesa" {
softwareType = "Mesa"
2024-07-06 17:37:07 +02:00
}
2024-07-06 20:37:14 +02:00
categoryID := config . Discord . CategoryID
2024-07-06 18:57:43 +02:00
guildID := i . GuildID
userID := i . Member . User . ID
2024-07-06 20:37:14 +02:00
userName := getUsernameFromMember ( i . Member )
if ! canCreateTicket ( userName , selectedOption ) {
err := client . InteractionRespond ( i . Interaction , & discordgo . InteractionResponse {
Type : discordgo . InteractionResponseChannelMessageWithSource ,
Data : & discordgo . InteractionResponseData {
Content : fmt . Sprintf ( "You already have an active %s ticket. Please wait for the administrators to process it." , softwareType ) ,
Flags : discordgo . MessageFlagsEphemeral ,
} ,
} )
if err != nil {
log . Println ( "Error sending interaction response:" , err )
}
return
}
2024-07-06 18:57:43 +02:00
2024-07-06 21:54:33 +02:00
_ , baseUrl := getTableNameAndBaseURL ( selectedOption )
userUID , _ := fetchUserID ( userName , baseUrl )
if userUID == 0 {
err := client . InteractionRespond ( i . Interaction , & discordgo . InteractionResponse {
Type : discordgo . InteractionResponseChannelMessageWithSource ,
Data : & discordgo . InteractionResponseData {
Content : fmt . Sprintf ( "Account not found for %s" , softwareType ) ,
Flags : discordgo . MessageFlagsEphemeral ,
} ,
} )
if err != nil {
log . Println ( "Error sending interaction response:" , err )
}
return
}
2024-07-06 18:57:43 +02:00
channel , err := client . GuildChannelCreateComplex ( guildID , discordgo . GuildChannelCreateData {
Name : fmt . Sprintf ( "reset-%s-%s" , softwareType , userName ) ,
Type : discordgo . ChannelTypeGuildText ,
2024-07-06 20:37:14 +02:00
ParentID : categoryID ,
2024-07-06 18:57:43 +02:00
PermissionOverwrites : [ ] * discordgo . PermissionOverwrite {
{
2024-07-06 20:44:16 +02:00
ID : guildID ,
Type : discordgo . PermissionOverwriteTypeRole ,
Deny : discordgo . PermissionViewChannel ,
2024-07-06 18:57:43 +02:00
} ,
} ,
} )
2024-07-06 17:37:07 +02:00
if err != nil {
2024-07-06 18:57:43 +02:00
log . Println ( "Error creating hwid request channel:" , err )
return
2024-07-06 17:37:07 +02:00
}
2024-07-06 18:57:43 +02:00
2024-07-06 21:01:44 +02:00
for _ , roleID := range config . Discord . AdminRoles {
2024-07-06 21:08:44 +02:00
if roleID != "" {
err := client . ChannelPermissionSet ( channel . ID , roleID , discordgo . PermissionOverwriteTypeRole , discordgo . PermissionViewChannel | discordgo . PermissionSendMessages , 0 )
if err != nil {
log . Printf ( "Error setting permissions for role %s: %v" , roleID , err )
}
2024-07-06 21:01:44 +02:00
}
2024-07-06 21:08:44 +02:00
2024-07-06 19:22:38 +02:00
}
2024-07-06 18:57:43 +02:00
err = client . InteractionRespond ( i . Interaction , & discordgo . InteractionResponse {
Type : discordgo . InteractionResponseChannelMessageWithSource ,
Data : & discordgo . InteractionResponseData {
2024-07-06 19:22:38 +02:00
Content : "Request submitted, you'll get a PM when it has been processed" ,
Flags : discordgo . MessageFlagsEphemeral ,
} ,
} )
if err != nil {
log . Println ( "Error sending interaction response:" , err )
return
}
_ , err = client . ChannelMessageSendComplex ( channel . ID , & discordgo . MessageSend {
2024-07-06 22:23:46 +02:00
Content : fmt . Sprintf ( "Reset request by <@%s> for %s" , userID , softwareType ) ,
2024-07-06 19:22:38 +02:00
Components : [ ] discordgo . MessageComponent {
discordgo . ActionsRow {
Components : [ ] discordgo . MessageComponent {
discordgo . Button {
Label : "Accept" ,
Style : discordgo . PrimaryButton ,
2024-07-06 21:54:33 +02:00
CustomID : fmt . Sprintf ( "accept_%s_%s_%d" , userID , softwareType , userUID ) ,
2024-07-06 19:22:38 +02:00
} ,
discordgo . Button {
Label : "Decline" ,
Style : discordgo . DangerButton ,
2024-07-06 21:54:33 +02:00
CustomID : fmt . Sprintf ( "decline_%s_%s_%d" , userID , softwareType , userUID ) ,
2024-07-06 19:22:38 +02:00
} ,
} ,
} ,
} ,
} )
if err != nil {
log . Println ( "Error sending message to the ticket channel:" , err )
}
} ,
2024-07-06 20:07:07 +02:00
"accept" : func ( client * discordgo . Session , i * discordgo . InteractionCreate ) {
data := i . MessageComponentData ( ) . CustomID
parts := strings . Split ( data , "_" )
2024-07-06 21:54:33 +02:00
if len ( parts ) != 4 {
2024-07-06 20:07:07 +02:00
log . Println ( "Invalid accept button custom ID" )
return
}
userID := parts [ 1 ]
softwareType := parts [ 2 ]
2024-07-06 21:54:33 +02:00
userUID , _ := strconv . Atoi ( parts [ 3 ] )
2024-07-06 20:07:07 +02:00
member , err := client . GuildMember ( i . GuildID , userID )
if err != nil {
log . Println ( "Error fetching member info:" , err )
return
}
2024-07-06 20:37:14 +02:00
userName := getUsernameFromMember ( member )
2024-07-06 20:07:07 +02:00
reset ( userName , softwareType )
2024-07-06 21:54:33 +02:00
tableName , _ := getTableNameAndBaseURL ( strings . ToLower ( softwareType ) )
2024-07-18 14:48:16 +02:00
successes , errors := resetAndVerify ( tableName , [ ] int { userUID } )
if len ( errors ) > 0 {
for _ , err := range errors {
log . Println ( "Error resetting hwid:" , err )
}
} else {
for _ , success := range successes {
if success {
log . Printf ( "Reset successful for UID %d" , userUID )
} else {
log . Printf ( "Reset unsuccessful for UID %d" , userUID )
}
}
2024-07-06 21:54:33 +02:00
}
log . Printf ( "Reset the HWID of user %s with UID %d for %s" , userName , userUID , softwareType )
2024-07-06 20:07:07 +02:00
err = client . InteractionRespond ( i . Interaction , & discordgo . InteractionResponse {
Type : discordgo . InteractionResponseChannelMessageWithSource ,
Data : & discordgo . InteractionResponseData {
Content : "Request accepted and processed." ,
Flags : discordgo . MessageFlagsEphemeral ,
} ,
} )
if err != nil {
log . Println ( "Error sending interaction response:" , err )
}
2024-07-06 20:37:14 +02:00
_ , err = client . ChannelDelete ( i . ChannelID )
2024-07-06 20:07:07 +02:00
if err != nil {
log . Println ( "Error deleting channel:" , err )
}
dmChannel , err := client . UserChannelCreate ( userID )
if err != nil {
log . Println ( "Error creating DM channel:" , err )
return
}
_ , err = client . ChannelMessageSend ( dmChannel . ID , fmt . Sprintf ( "Your reset request for %s has been accepted and processed." , softwareType ) )
if err != nil {
log . Println ( "Error sending DM:" , err )
}
} ,
2024-07-06 19:22:38 +02:00
"decline" : func ( client * discordgo . Session , i * discordgo . InteractionCreate ) {
data := i . MessageComponentData ( ) . CustomID
parts := strings . Split ( data , "_" )
2024-07-06 21:54:33 +02:00
if len ( parts ) != 4 {
2024-07-06 19:22:38 +02:00
log . Println ( "Invalid decline button custom ID" )
return
}
userID := parts [ 1 ]
err := client . InteractionRespond ( i . Interaction , & discordgo . InteractionResponse {
Type : discordgo . InteractionResponseChannelMessageWithSource ,
Data : & discordgo . InteractionResponseData {
Content : "Request declined." ,
2024-07-06 18:57:43 +02:00
Flags : discordgo . MessageFlagsEphemeral ,
} ,
} )
2024-07-06 17:37:07 +02:00
if err != nil {
2024-07-06 18:57:43 +02:00
log . Println ( "Error sending interaction response:" , err )
2024-07-06 17:37:07 +02:00
}
2024-07-06 19:22:38 +02:00
2024-07-06 20:37:14 +02:00
_ , err = client . ChannelDelete ( i . ChannelID )
2024-07-06 20:07:07 +02:00
if err != nil {
log . Println ( "Error deleting channel:" , err )
}
2024-07-06 19:22:38 +02:00
dmChannel , err := client . UserChannelCreate ( userID )
if err != nil {
log . Println ( "Error creating DM channel:" , err )
return
}
_ , err = client . ChannelMessageSend ( dmChannel . ID , "Your reset request has been declined." )
if err != nil {
log . Println ( "Error sending DM:" , err )
}
2024-07-06 17:37:07 +02:00
} ,
}
)
2024-07-18 14:28:20 +02:00
func resetCommandHandler ( client * discordgo . Session , i * discordgo . InteractionCreate , softwareType string ) {
hasAdminPermission := false
var err error
var tableName string
for _ , roleID := range config . Discord . AdminRoles {
member , err := client . GuildMember ( i . GuildID , i . Member . User . ID )
if err != nil {
log . Println ( "Error fetching member info:" , err )
return
}
for _ , role := range member . Roles {
if role == roleID {
hasAdminPermission = true
break
}
}
if hasAdminPermission {
break
}
}
if ! hasAdminPermission {
err := client . InteractionRespond ( i . Interaction , & discordgo . InteractionResponse {
Type : discordgo . InteractionResponseChannelMessageWithSource ,
Data : & discordgo . InteractionResponseData {
Content : "You do not have permission to run this command." ,
Flags : discordgo . MessageFlagsEphemeral ,
} ,
} )
if err != nil {
log . Println ( "Error sending interaction response:" , err )
}
return
}
identifier := i . ApplicationCommandData ( ) . Options [ 0 ] . StringValue ( )
2024-07-18 14:48:16 +02:00
identifierList := strings . Split ( identifier , "," )
2024-07-18 14:28:20 +02:00
2024-07-18 14:48:16 +02:00
var userUIDs [ ] int
2024-07-18 14:28:20 +02:00
2024-07-18 14:48:16 +02:00
for _ , identifier := range identifierList {
identifier = strings . TrimSpace ( identifier )
var userName string
var userUID int
var err error
if uid , err := strconv . Atoi ( identifier ) ; err == nil {
userUID = uid
} else {
userName = identifier
}
if userName != "" {
_ , baseURL := getTableNameAndBaseURL ( softwareType )
userUID , err = fetchUserID ( userName , baseURL )
2024-07-18 14:28:20 +02:00
if err != nil {
2024-07-18 14:48:16 +02:00
log . Println ( "Error fetching user ID:" , err )
continue
2024-07-18 14:28:20 +02:00
}
2024-07-18 14:48:16 +02:00
}
if userUID != 0 {
userUIDs = append ( userUIDs , userUID )
2024-07-18 14:28:20 +02:00
}
}
2024-07-18 14:48:16 +02:00
if len ( userUIDs ) == 0 {
err := client . InteractionRespond ( i . Interaction , & discordgo . InteractionResponse {
2024-07-18 14:28:20 +02:00
Type : discordgo . InteractionResponseChannelMessageWithSource ,
Data : & discordgo . InteractionResponseData {
2024-07-18 14:48:16 +02:00
Content : "No valid users found to reset." ,
2024-07-18 14:28:20 +02:00
Flags : discordgo . MessageFlagsEphemeral ,
} ,
} )
if err != nil {
log . Println ( "Error sending interaction response:" , err )
}
return
}
2024-07-18 14:48:16 +02:00
tableName , _ = getTableNameAndBaseURL ( softwareType )
successes , errors := resetAndVerify ( tableName , userUIDs )
if len ( errors ) > 0 {
for _ , err := range errors {
log . Println ( "Error:" , err )
}
} else {
for i , success := range successes {
if success {
log . Printf ( "Reset successful for UID %d" , userUIDs [ i ] )
} else {
log . Printf ( "Reset unsuccessful for UID %d" , userUIDs [ i ] )
}
}
}
2024-07-18 14:28:20 +02:00
2024-07-18 14:57:16 +02:00
for _ , uid := range userUIDs {
log . Printf ( "Reset the HWID of user with UID %d for %s" , uid , softwareType )
}
2024-07-18 14:28:20 +02:00
err = client . InteractionRespond ( i . Interaction , & discordgo . InteractionResponse {
Type : discordgo . InteractionResponseChannelMessageWithSource ,
Data : & discordgo . InteractionResponseData {
2024-07-18 14:48:16 +02:00
Content : fmt . Sprintf ( "Successfully reset HWID for %d users." , len ( userUIDs ) ) ,
2024-07-18 14:28:20 +02:00
Flags : discordgo . MessageFlagsEphemeral ,
} ,
} )
if err != nil {
log . Println ( "Error sending interaction response:" , err )
}
}
2024-07-06 20:37:14 +02:00
func canCreateTicket ( userName , softwareType string ) bool {
guildChannels , err := client . GuildChannels ( config . Discord . GuildID )
if err != nil {
log . Println ( "Error fetching guild channels:" , err )
return false
}
for _ , channel := range guildChannels {
2024-07-06 20:44:16 +02:00
if channel . Type == discordgo . ChannelTypeGuildText && channel . ParentID == config . Discord . CategoryID {
2024-07-06 20:37:14 +02:00
expectedName := fmt . Sprintf ( "reset-%s-%s" , softwareType , userName )
if channel . Name == expectedName {
2024-07-06 20:44:16 +02:00
return false
2024-07-06 20:37:14 +02:00
}
}
}
return true
}
2024-07-06 21:54:33 +02:00
func getTableNameAndBaseURL ( choice string ) ( string , string ) {
var tableName , baseURL string
switch choice {
case "vanity" :
tableName = "AuthUserData"
baseURL = "http://vanitycheats.xyz/UserAuthentication.php"
case "mesa" :
tableName = "AuthUserData-Mesachanger.com"
baseURL = "http://mesachanger.com/UserAuthentication.php"
default :
fmt . Println ( "Invalid choice. Please choose 'vanity' or 'mesa'." )
}
return tableName , baseURL
}
func fetchUserID ( username string , baseURL string ) ( int , error ) {
requestBody := & bytes . Buffer { }
multiPartWriter := multipart . NewWriter ( requestBody )
err := multiPartWriter . WriteField ( "username" , username )
if err != nil {
return 0 , fmt . Errorf ( "error adding username field: %w" , err )
}
multiPartWriter . Close ( )
request , err := http . NewRequest ( "POST" , baseURL , requestBody )
if err != nil {
return 0 , fmt . Errorf ( "error creating request: %w" , err )
}
request . Header . Set ( "Content-Type" , multiPartWriter . FormDataContentType ( ) )
client := & http . Client {
Timeout : 10 * time . Second ,
}
resp , err := client . Do ( request )
if err != nil {
return 0 , fmt . Errorf ( "error sending request: %w" , err )
}
defer resp . Body . Close ( )
var userResp UserResponse
err = json . NewDecoder ( resp . Body ) . Decode ( & userResp )
if err != nil {
return 0 , fmt . Errorf ( "error decoding JSON: %w" , err )
}
uid , err := strconv . Atoi ( userResp . ID )
if err != nil {
return 0 , fmt . Errorf ( "error converting user ID: %w" , err )
}
return uid , nil
}
func resetHWID ( tableName string , uids [ ] int ) {
query := fmt . Sprintf ( ` UPDATE public . % q
SET "StorageIdentifier" = NULL , "BootIdentifier" = NULL
WHERE "UID" = ANY ( $ 1 ) ` , tableName )
_ , err := db . Exec ( query , pq . Array ( uids ) )
if err != nil {
log . Printf ( "Error resetting HWID: %v" , err )
return
}
}
2024-07-18 14:48:16 +02:00
func resetAndVerify ( tableName string , uids [ ] int ) ( [ ] bool , [ ] error ) {
var successes [ ] bool
var errorsSlice [ ] error
rows , err := db . Query ( fmt . Sprintf ( ` SELECT "UID", MD5(CONCAT("StorageIdentifier", "BootIdentifier")) FROM public.%q WHERE "UID" = ANY($1) ` , tableName ) , pq . Array ( uids ) )
2024-07-06 21:54:33 +02:00
if err != nil {
2024-07-18 14:48:16 +02:00
log . Printf ( "Error querying database for before hashes: %v" , err )
return nil , [ ] error { err }
}
defer rows . Close ( )
beforeHashesMap := make ( map [ int ] string )
for rows . Next ( ) {
var uid int
var beforeHash string
err := rows . Scan ( & uid , & beforeHash )
if err != nil {
log . Printf ( "Error scanning rows: %v" , err )
errorsSlice = append ( errorsSlice , err )
continue
2024-07-06 21:54:33 +02:00
}
2024-07-18 14:48:16 +02:00
beforeHashesMap [ uid ] = beforeHash
2024-07-06 21:54:33 +02:00
}
2024-07-18 14:48:16 +02:00
resetHWID ( tableName , uids )
rows , err = db . Query ( fmt . Sprintf ( ` SELECT "UID", MD5(CONCAT("StorageIdentifier", "BootIdentifier")) FROM public.%q WHERE "UID" = ANY($1) ` , tableName ) , pq . Array ( uids ) )
2024-07-06 21:54:33 +02:00
if err != nil {
2024-07-18 14:48:16 +02:00
log . Printf ( "Error querying database for after hashes: %v" , err )
return nil , [ ] error { err }
}
defer rows . Close ( )
afterHashesMap := make ( map [ int ] string )
for rows . Next ( ) {
var uid int
var afterHash string
err := rows . Scan ( & uid , & afterHash )
if err != nil {
log . Printf ( "Error scanning rows: %v" , err )
errorsSlice = append ( errorsSlice , err )
continue
}
afterHashesMap [ uid ] = afterHash
}
for _ , uid := range uids {
afterHash , ok := afterHashesMap [ uid ]
if ! ok {
errorsSlice = append ( errorsSlice , fmt . Errorf ( "no rows found for UID %d after reset" , uid ) )
successes = append ( successes , false )
continue
2024-07-06 21:54:33 +02:00
}
2024-07-18 14:48:16 +02:00
successes = append ( successes , beforeHashesMap [ uid ] != afterHash || afterHash == "d41d8cd98f00b204e9800998ecf8427e" )
2024-07-06 21:54:33 +02:00
}
2024-07-18 14:48:16 +02:00
return successes , errorsSlice
2024-07-06 21:54:33 +02:00
}
2024-07-06 16:55:30 +02:00
func main ( ) {
if client == nil {
2024-07-06 17:16:13 +02:00
log . Println ( "Bot client is not initialized" )
2024-07-06 16:55:30 +02:00
return
}
client . AddHandler ( func ( client * discordgo . Session , r * discordgo . Ready ) {
2024-07-06 17:16:13 +02:00
log . Println ( "Bot is online" )
2024-07-06 18:06:55 +02:00
cmdIDs := make ( map [ string ] string , len ( commands ) )
for _ , cmd := range commands {
rcmd , err := client . ApplicationCommandCreate ( client . State . User . ID , config . Discord . GuildID , & cmd )
if err != nil {
log . Fatalf ( "Cannot create slash command %q: %v" , cmd . Name , err )
}
cmdIDs [ rcmd . ID ] = rcmd . Name
}
2024-07-06 16:55:30 +02:00
} )
2024-07-06 17:37:07 +02:00
client . AddHandler ( func ( client * discordgo . Session , i * discordgo . InteractionCreate ) {
2024-07-06 18:57:43 +02:00
if i . Type == discordgo . InteractionApplicationCommand {
if h , ok := commandsHandlers [ i . ApplicationCommandData ( ) . Name ] ; ok {
h ( client , i )
}
} else if i . Type == discordgo . InteractionMessageComponent {
2024-07-06 19:22:38 +02:00
customID := i . MessageComponentData ( ) . CustomID
if h , ok := componentsHandlers [ customID ] ; ok {
2024-07-06 18:57:43 +02:00
h ( client , i )
2024-07-06 19:22:38 +02:00
} else if strings . HasPrefix ( customID , "accept_" ) {
componentsHandlers [ "accept" ] ( client , i )
} else if strings . HasPrefix ( customID , "decline_" ) {
componentsHandlers [ "decline" ] ( client , i )
2024-07-06 18:57:43 +02:00
}
2024-07-06 17:37:07 +02:00
}
} )
2024-07-06 16:55:30 +02:00
err := client . Open ( )
if err != nil {
2024-07-06 17:16:13 +02:00
log . Println ( "Error opening connection:" , err )
2024-07-06 16:55:30 +02:00
return
2024-07-06 18:57:43 +02:00
}
2024-07-06 16:55:30 +02:00
stop := make ( chan os . Signal , 1 )
signal . Notify ( stop , os . Interrupt , syscall . SIGTERM )
<- stop
2024-07-06 17:16:13 +02:00
log . Println ( "Gracefully shutting down." )
2024-07-06 16:55:30 +02:00
client . Close ( )
2024-07-06 17:27:04 +02:00
if db != nil {
db . Close ( )
}
2024-07-06 16:55:30 +02:00
}