lets go pikachu

This commit is contained in:
Purrfxct 2025-01-25 20:30:30 +00:00
parent ff0bde844a
commit fe282d2e77

264
dev.go Normal file
View File

@ -0,0 +1,264 @@
package main
import (
"bufio"
"encoding/json"
"fmt"
"math/rand"
"net"
"net/http"
"os"
"strings"
"sync"
"time"
"io"
"golang.org/x/net/html"
)
type ScanState struct {
ValidInviteCount int `json:"valid_invite_count"`
ScannedInviteCount int `json:"scanned_invite_count"`
ErrorCount int `json:"error_count"`
StartTime time.Time `json:"-"`
ValidServers []ValidServerInfo `json:"valid_servers"`
}
type ValidServerInfo struct {
InviteURL string `json:"invite_url"`
ServerName string `json:"server_name"`
Members int `json:"members"`
}
var (
server = "irc.supernets.org"
port = "6667"
channel = "#superbowl"
outputChannel = "#guttertarts"
nickname = "x"
nickservPassword = "x"
httpClient = &http.Client{Timeout: 10 * time.Second}
state ScanState
stateMutex sync.Mutex
inviteLog *os.File
)
func handleIRCConnection(conn net.Conn, connected chan bool) {
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, ":End of /MOTD command.") {
connected <- true
}
if strings.HasPrefix(line, "PING") {
pongResponse := strings.Split(line, " ")[1]
ircSend(conn, fmt.Sprintf("PONG %s", pongResponse))
}
}
if err := scanner.Err(); err != nil {
fmt.Printf("Connection error: %v\n", err)
}
}
func checkInvite(url string, conn net.Conn) {
stateMutex.Lock()
state.ScannedInviteCount++
stateMutex.Unlock()
resp, err := httpClient.Get(url)
if err != nil {
stateMutex.Lock()
state.ErrorCount++
stateMutex.Unlock()
fmt.Printf("Error fetching invite: %v\n", err)
return
}
defer resp.Body.Close()
if resp.StatusCode == 200 {
serverName, members, isValid := parseDiscordHTML(resp.Body)
if isValid {
stateMutex.Lock()
state.ValidInviteCount++
state.ValidServers = append(state.ValidServers, ValidServerInfo{
InviteURL: url,
ServerName: serverName,
Members: members,
})
stateMutex.Unlock()
logMessage := fmt.Sprintf("Valid invite: %s | Server: %s | Members: %d\n",
url, serverName, members)
ircMessage := fmt.Sprintf("PRIVMSG %s :Valid invite: %s | Server: %s | Members: %d",
outputChannel, url, serverName, members)
fmt.Fprint(inviteLog, logMessage)
ircSend(conn, ircMessage)
} else {
fmt.Printf("Invalid invite (no 'Join the' phrase): %s\n", url)
}
} else {
fmt.Printf("Invalid invite: %s\n", url)
}
}
func parseDiscordHTML(bodyReader io.Reader) (string, int, bool) {
doc, err := html.Parse(bodyReader)
if err != nil {
fmt.Printf("Error parsing HTML: %v\n", err)
return "", 0, false
}
var serverName string
var members int
var hasJoinThe bool
var parse func(*html.Node)
parse = func(n *html.Node) {
if n.Type == html.ElementNode && n.Data == "meta" {
var property, name, content string
for _, attr := range n.Attr {
if attr.Key == "property" {
property = attr.Val
} else if attr.Key == "name" {
name = attr.Val
} else if attr.Key == "content" {
content = attr.Val
}
}
// Check for server name in og:title
if property == "og:title" {
serverName = strings.TrimSpace(strings.Replace(content, "Join the", "", 1))
serverName = strings.Replace(serverName, "Discord Server!", "", 1)
serverName = strings.TrimSpace(serverName)
}
// Check for member count in description
if name == "description" {
if strings.Contains(content, "members") {
parts := strings.Split(content, "|")
for _, part := range parts {
part = strings.TrimSpace(part)
if strings.HasSuffix(part, "members") {
fmt.Sscanf(part, "%d members", &members)
break
}
}
} else if strings.Contains(content, "other members") {
fmt.Sscanf(content, "%d other members", &members)
}
}
// Check for "Join the" phrase
if strings.Contains(content, "Join the") {
hasJoinThe = true
}
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
parse(c)
}
}
parse(doc)
return serverName, members, hasJoinThe
}
func ircSend(conn net.Conn, message string) {
fmt.Fprintf(conn, "%s\r\n", message)
fmt.Printf("[IRC Command Sent]: %s\n", message)
}
func generateRandomCode() string {
length := rand.Intn(9) + 2
letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
code := make([]rune, length)
for i := range code {
code[i] = letters[rand.Intn(len(letters))]
}
return string(code)
}
func startWebDashboard() {
http.HandleFunc("/stats", func(w http.ResponseWriter, r *http.Request) {
stateMutex.Lock()
defer stateMutex.Unlock()
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(state)
})
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
stateMutex.Lock()
defer stateMutex.Unlock()
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "<html><head><title>Invite Scanner</title></head><body>")
fmt.Fprintf(w, "<h1>Discord Invite Scanner Dashboard</h1>")
fmt.Fprintf(w, "<ul>")
fmt.Fprintf(w, "<li><strong>Valid Invites:</strong> %d</li>", state.ValidInviteCount)
fmt.Fprintf(w, "<li><strong>Scanned Invites:</strong> %d</li>", state.ScannedInviteCount)
fmt.Fprintf(w, "<li><strong>Error Count:</strong> %d</li>", state.ErrorCount)
fmt.Fprintf(w, "<li><strong>Scanning Duration:</strong> %s</li>", time.Since(state.StartTime).Truncate(time.Second))
fmt.Fprintf(w, "<li><strong>Valid Servers:</strong></li>")
fmt.Fprintf(w, "<ul>")
for _, server := range state.ValidServers {
fmt.Fprintf(w, "<li><strong>Invite:</strong> <a href='%s'>%s</a> | <strong>Server Name:</strong> %s | <strong>Members:</strong> %d</li>", server.InviteURL, server.InviteURL, server.ServerName, server.Members)
}
fmt.Fprintf(w, "</ul>")
fmt.Fprintf(w, "</ul>")
fmt.Fprintf(w, "</body></html>")
})
fmt.Println("Web dashboard running on http://localhost:55555")
http.ListenAndServe(":55555", nil)
}
func main() {
var err error
inviteLog, err = os.OpenFile("valid_invites.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Printf("Error opening log file: %v\n", err)
return
}
defer inviteLog.Close()
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%s", server, port))
if err != nil {
fmt.Printf("Error connecting to IRC server: %v\n", err)
return
}
defer conn.Close()
connected := make(chan bool)
go handleIRCConnection(conn, connected)
ircSend(conn, fmt.Sprintf("NICK %s", nickname))
ircSend(conn, fmt.Sprintf("USER %s 0 * :%s", nickname, nickname))
<-connected
ircSend(conn, fmt.Sprintf("PRIVMSG NickServ :IDENTIFY %s", nickservPassword))
time.Sleep(5 * time.Second)
ircSend(conn, fmt.Sprintf("JOIN %s", channel))
time.Sleep(5 * time.Second)
ircSend(conn, fmt.Sprintf("JOIN %s", outputChannel))
state.StartTime = time.Now()
go startWebDashboard()
ticker := time.NewTicker(10 * time.Minute)
defer ticker.Stop()
for {
select {
case <-ticker.C:
duration := time.Since(state.StartTime)
statusMessage := fmt.Sprintf(
"Status: Time Scanning: %s | Invites Scanned: %d | Valid Invites: %d",
duration.Truncate(time.Second), state.ScannedInviteCount, state.ValidInviteCount,
)
ircSend(conn, fmt.Sprintf("PRIVMSG %s :%s", channel, statusMessage))
case <-time.After(time.Duration(rand.Intn(30)+1) * time.Second):
randomCode := generateRandomCode()
inviteURL := fmt.Sprintf("https://discord.com/invite/%s", randomCode)
checkInvite(inviteURL, conn)
}
}
}