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)
|
|
|
|
}
|