257 lines
16 KiB
Go
Raw Normal View History

2019-08-05 16:57:01 -07:00
package main
import (
"context"
"fmt"
2021-05-16 22:00:21 -07:00
"io/ioutil"
2019-08-05 16:57:01 -07:00
"log"
"math/rand"
2020-11-18 13:24:28 -08:00
"net/http"
2019-08-05 16:57:01 -07:00
"os"
"strconv"
"strings"
2019-10-20 13:08:35 -07:00
"time"
2019-08-05 16:57:01 -07:00
2020-08-15 15:59:04 -07:00
"golang.org/x/net/proxy"
2019-08-05 16:57:01 -07:00
irc "gopkg.in/irc.v3"
)
var insults = []string{"Leopard", "SailfinMolly", "NarwhalMan", "BluejayMan", "GiantCaveSwallow", "GreatHornedOwl", "SnappingTurtleMan", "Wolverine", "GiantAardvark", "SeaLamprey", "GrasshopperMan", "WhitebrowedGibbon", "GiantLouse", "RedwingedBlackbird", "Fairy", "LorikeetMan", "GiantSkunk", "GiantAlligator", "CaveFish", "Mosquito", "GiantFox", "SaltwaterCrocodileMan", "GiantOsprey", "Lion", "WeaselMan", "Human", "Swordfish", "ClownLoach", "DragonflyMan", "GiantMosquito", "SpongeMan", "GiantCopperheadSnake", "GiantBeetle", "KestrelMan", "BullShark", "AardvarkMan", "PeachfacedLovebirdMan", "GiantThrips", "Capybara", "Rutherer", "MaskedLovebirdMan", "KnuckleWorm", "OnehumpedCamelMan", "Deer", "Cassowary", "BaskingShark", "Wombat", "GiantSkink", "GiantMongoose", "EchidnaMan", "ParakeetMan", "HareMan", "BlackcrestedGibbon", "VultureMan", "Slug", "BushmasterMan", "GiantBobcat", "GiganticPanda", "CapybaraMan", "MagmaMan", "SquidMan", "GiantCrab", "ChinchillaMan", "GiantOstrich", "GiantWhiteStork", "GiantDeer", "RedPanda", "Siamang", "Dwarf", "GiantLeopard", "Bobcat", "Duck", "TwohumpedCamelMan", "GiantBeaver", "Troglodyte", "GiantRattlesnake", "RoachMan", "Satyr", "Albatross", "CheetahMan", "SpottedRatfish", "AdderMan", "GrayLangur", "CockatielMan", "GiantMuskox", "GiantKea", "CaveBlob", "GiantWren", "SeaNettleJellyfish", "GiantMoth", "GiantDingo", "SpiderMonkeyMan", "Oyster", "GiantMaskedLovebird", "GiantSaltwaterCrocodile", "GiantKoala", "BrownRecluseSpiderMan", "MoonSnailMan", "GiantMagpie", "FoxSquirrel", "GiantLionTamarin", "Halibut", "Clownfish", "BobcatMan", "Groundhog", "DarkGnome", "GiantCapuchin", "GiantPlatypus", "HamsterMan", "BrownRecluseSpider", "Harpy", "BloodGnat", "Molemarian", "GroundhogMan", "GiantGrayLangur", "Beetle", "GiantChinchilla", "Buzzard", "BlackMamba", "BeakDog", "Magpie", "DesertTortoiseMan", "ChameleonMan", "IguanaMan", "TigerMan", "BarkScorpion", "GiantAlbatross", "GiantTiger", "Guppy", "GiantSnappingTurtle", "Mantis", "Raccoon", "Cardinal", "RhesusMacaque", "RatMan", "GiantOcelot", "GiantGrizzlyBear", "MountainGoatMan", "Carp", "Beaver", "Tiger", "GiantOtter", "ToadMan", "GreatBarracuda", "MonarchButterfly", "Thrips", "SlugMan", "GiantCaveToad", "GreenTreeFrog", "Moghopper", "GiantKingsnake", "KingCobraMan", "SpermWhaleMan", "Capuchin", "PondGrabber", "MooseCow", "Vulture", "RhinocerosMan", "BlackMambaMan", "GiantBat", "GiantAxolotl", "WhitetipReefShark", "GiantOrca", "Louse", "GrayLangurMan", "Snail", "Tapir", "Herring", "Nautilus", "SeaOtter", "Anole", "SkunkMan", "TwohumpedCamel", "GiantMooseBull", "RedPandaMan", "Damselfly", "Orca", "FlyingSquirrel", "KoalaMan", "GreenDevourer", "WildBoar", "Ibex", "GiantLion", "SeaSerpent", "ReptileMan", "LizardMan", "GiantNarwhal", "GiantMandrill", "Perch", "Oriole", "Hamster", "GiantRat", "GiantRaven", "Wagon", "GiantLizard", "GiantCougar", "Axolotl", "GiantWeasel", "WrenMan", "Creature", "GiantCardinal", "Kobold", "GiantEarthworm", "Cougar", "SnowyOwl", "BarnOwlMan", "PuffinMan", "GiantCoyote", "Hake", "GiantNautilus", "HornbillMan", "Bilou", "LeopardSealMan", "GreyParrotMan", "Unicorn", "GiantOpossum", "Stingray", "Bat", "Kangaroo", "GiantMooseCow", "PurringMaggot", "Impala", "CaveFloater", "Horse", "SwanMan", "Gazelle", "FoulBlendec", "LouseMan", "Elk", "Llama", "PorcupineMan", "PeregrineFalcon", "Lorikeet", "NakedMoleDog", "CaveSpider", "MagpieMan", "CrabMan", "GiantGilaMonster", "GiantCapybara", "Lizard", "HoaryMarmotMan", "WolfMan", "Kingsnake", "CapuchinMan", "GiantAnaconda", "GiantHornbill", "GiantToad", "NautilusMan", "GiantGraySquirrel", "TapirMan", "GiantSnowyOwl", "Bushtit", "GiantWalrus", "GiantOriole", "GiantParakeet", "HoneyBadger", "Cyclops", "SpinyDogfish", "Coati", "GiantRedSquirrel", "WhitespottedPuffer", "OspreyMan", "MothMan", "Chimpanzee", "Mink", "Walrus", "Tigerfish", "GiantDesertTortoise", "DeerMan", "BlueJay", "Sole", "Cat", "ElkMan", "Cow", "IceWolf", "Fly", "Crow", "BatRay", "MagmaCrab", "Angelshark", "Anchovy", "MonarchButterflyMan", "Bonobo", "BarnOwl", "GiganticTortoise", "Seahorse", "Badger", "GilaMonsterMan", "Hydra",
2021-05-16 22:00:21 -07:00
var friends = []string{}
2019-08-05 16:57:01 -07:00
var s = rand.NewSource(time.Now().UnixNano()) // initialize global pseudo random generator
var r = rand.New(s)
var channel string
var victim string
2020-11-18 13:24:28 -08:00
var is_connected = false
2021-05-16 22:00:21 -07:00
var op_mode = false
2020-11-18 13:24:28 -08:00
var greetChance = 100
var suffixLength = 0
2021-05-16 22:00:21 -07:00
func loadFriends() {
friendsV := os.Getenv("IRC_EXTRAFRIENDS")
if friendsV == "" {
return
}
for _, name := range strings.Split(strings.TrimSpace(friendsV), ",") {
name := strings.TrimSpace(name)
if name != "" {
log.Printf("loaded extra friend: %s", name)
friends = append(friends, name)
}
}
}
func loadNames(fpath string) {
content, err := ioutil.ReadFile(fpath)
if err != nil {
panic(err)
}
insults = []string{}
for _, line := range strings.Split(string(content), "\n") {
stripped := strings.TrimSpace(line)
if stripped != "" {
log.Printf("loaded name: %s", stripped)
insults = append(insults, stripped)
}
}
if len(insults) == 0 {
panic(fmt.Errorf("no names loaded from %s", fpath))
}
}
2020-11-18 13:24:28 -08:00
func getNumericVar(key string, default_ int) int {
varS := os.Getenv(key)
if varS != "" {
var err error
default_, err = strconv.Atoi(varS)
if err != nil {
panic(fmt.Errorf("invalid %s: '%s'", key, varS))
}
}
return default_
}
2021-05-16 22:00:21 -07:00
func friendlyNick(nick string) bool {
for _, n := range friends {
if nick == n {
return true
}
}
for _, n := range insults {
if strings.HasPrefix(nick, n) {
return true
}
}
if strings.HasPrefix(nick, "Op") {
return friendlyNick(nick[2:])
}
return false
}
2020-11-18 13:24:28 -08:00
func genNick() string {
2021-05-16 22:00:21 -07:00
n := insults[r.Intn(len(insults))] + trailingDigits(suffixLength)
if op_mode {
n = "Op" + n
}
return n
2020-11-18 13:24:28 -08:00
}
func trailingDigits(length int) string {
trailing := ""
2021-05-16 22:00:21 -07:00
for ; length > 0; length-- {
2020-11-18 13:24:28 -08:00
trailing = fmt.Sprintf("%s%d", trailing, r.Intn(10))
}
return trailing
}
func runStatusHandler() {
statusPort := getNumericVar("HEALTH_PORT", 8000)
if statusPort < 0 || statusPort > 65535 {
panic(fmt.Errorf("invalid HEALTH_PORT: %d", statusPort))
}
healthAddress := fmt.Sprintf("0.0.0.0:%d", statusPort)
log.Printf("serving health on %s", healthAddress)
http.HandleFunc("/health", statusHandler())
log.Fatal(http.ListenAndServe(healthAddress, nil))
}
func statusHandler() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if !is_connected {
http.Error(w, "Not connected", http.StatusInternalServerError)
return
}
w.Write([]byte("ok"))
}
}
2019-08-05 16:57:01 -07:00
func HandleRequest(ctx context.Context) (string, error) {
// So i was too lazy to get rid of the boilerplate
// Set up IRC connection
server := os.Getenv("IRC_SERVER")
if server == "" {
log.Fatal("Set IRC_SERVER!")
}
channel = os.Getenv("IRC_CHANNEL")
if channel == "" {
log.Fatal("Set IRC_CHANNEL!")
}
victim = os.Getenv("IRC_VICTIM")
if victim == "" {
log.Fatal("Set IRC_VICTIM!")
}
2021-05-16 22:00:21 -07:00
enable_op_mode := os.Getenv("IRC_OP_MODE")
if enable_op_mode != "" {
op_mode = true
}
2020-11-18 13:24:28 -08:00
suffixLength = getNumericVar("SUFFIX_DIGITS", 0)
if suffixLength < 0 || suffixLength > 10 {
panic(fmt.Errorf("invalid SUFFIX_DIGITS: %d", suffixLength))
}
log.Println("dialing...")
2019-08-05 16:57:01 -07:00
2020-11-18 13:24:28 -08:00
dialCtx, cancelDial := context.WithTimeout(context.Background(), time.Second*10)
defer cancelDial()
2020-08-15 15:59:04 -07:00
2020-11-18 13:24:28 -08:00
conn, err := proxy.Dial(dialCtx, "tcp", server)
2019-08-05 16:57:01 -07:00
if err != nil {
log.Fatal(err)
}
hostname, _ := os.Hostname()
config := irc.ClientConfig{
2020-11-18 13:24:28 -08:00
Nick: genNick(),
2019-08-05 16:57:01 -07:00
User: hostname,
2019-10-20 13:08:35 -07:00
Name: "dabbing on " + victim,
2019-08-05 16:57:01 -07:00
Handler: irc.HandlerFunc(handleIRCCon), // See below for callback
}
2020-11-18 13:24:28 -08:00
log.Println("starting client...")
2019-08-05 16:57:01 -07:00
client := irc.NewClient(conn, config)
err = client.Run()
return fmt.Sprintf("i died"), err
}
func handleIRCCon(c *irc.Client, m *irc.Message) {
2021-05-16 22:00:21 -07:00
log.Printf("%+v\n", m)
2019-08-05 16:57:01 -07:00
sent := 0
// Only one reader, prob not necessary
// Handle connection established
if m.Command == "001" {
2020-11-18 13:24:28 -08:00
is_connected = true
2019-08-05 16:57:01 -07:00
// 001 is welcome event
c.Write("JOIN " + channel)
} else if m.Command == "PRIVMSG" && c.FromChannel(m) {
// Handle chat message to print stats
text := strings.Split(m.Trailing(), " ")
if text[0] == c.CurrentNick()+":" && text[len(text)-1] == "stats" {
c.WriteMessage(&irc.Message{
Command: "PRIVMSG",
Params: []string{
m.Params[0],
"I've sent " + strconv.FormatInt(int64(sent), 10) + " invites to " + victim + " :)",
},
})
} else if text[len(text)-1] == "name" {
2020-11-18 13:24:28 -08:00
nick := genNick()
2019-08-05 16:57:01 -07:00
c.Write("NICK " + nick)
} else if text[len(text)-1] == "invite" {
c.Write("INVITE " + victim + " " + channel)
2019-10-20 13:08:35 -07:00
sent++
2020-11-18 13:24:28 -08:00
} /* else if strings.Contains(m.Trailing(), "greetings") {
2019-10-20 13:08:35 -07:00
c.WriteMessage(&irc.Message{
Command: "PRIVMSG",
Params: []string{
m.Params[0],
victim + ": hi",
},
})
2020-08-15 16:24:25 -07:00
}*/
} else if m.Command == "JOIN" && c.FromChannel(m) {
if m.Prefix.Name != c.CurrentNick() {
2021-05-16 22:00:21 -07:00
if greetChance == 1000 || r.Intn(1000) < greetChance {
2020-11-18 13:24:28 -08:00
c.WriteMessage(&irc.Message{
Command: "PRIVMSG",
Params: []string{
m.Params[0],
"greetings " + m.Prefix.Name,
},
})
}
2021-05-16 22:00:21 -07:00
if op_mode && friendlyNick(m.Prefix.Name) {
log.Printf("opping %s", m.Prefix.Name)
c.WriteMessage(&irc.Message{
Command: "MODE",
Params: []string{
m.Params[0],
"+o",
m.Prefix.Name,
},
})
}
2019-08-05 16:57:01 -07:00
}
}
}
func main() {
2020-11-18 13:24:28 -08:00
greetChance = getNumericVar("GREET_CHANCE", 100)
if greetChance > 100 || greetChance < 0 {
log.Fatalf("invalid GREET_CHANCE: '%s'", greetChance)
}
2021-05-16 22:00:21 -07:00
namesPath := os.Getenv("NAMES_FPATH")
if namesPath != "" {
loadNames(namesPath)
}
loadFriends()
2020-11-18 13:24:28 -08:00
go runStatusHandler()
2019-08-05 16:57:01 -07:00
ctx, _ := context.WithCancel(context.Background())
ret, err := HandleRequest(ctx)
if err != nil {
log.Fatalln(err)
}
log.Println(ret)
}