lets go pikachu
This commit is contained in:
parent
ff0bde844a
commit
fe282d2e77
264
dev.go
Normal file
264
dev.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user