Initial commit
This commit is contained in:
commit
955bedb8f9
|
@ -0,0 +1,3 @@
|
||||||
|
irc_bot
|
||||||
|
*.mmdb
|
||||||
|
*.json
|
|
@ -0,0 +1,33 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
IRCServer string `json:"irc_server"`
|
||||||
|
IRCPort int `json:"irc_port"`
|
||||||
|
SSL bool `json:"ssl"`
|
||||||
|
Nickname string `json:"nickname"`
|
||||||
|
Channels []string `json:"channels"`
|
||||||
|
GeoIPDatabase string `json:"geoip_database"`
|
||||||
|
GeoIPASN string `json:"geoip_asn"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadConfig(filePath string) (*Config, error) {
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
config := &Config{}
|
||||||
|
decoder := json.NewDecoder(file)
|
||||||
|
err = decoder.Decode(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"irc_server": "irc.network.example",
|
||||||
|
"irc_port": 6697,
|
||||||
|
"ssl": true,
|
||||||
|
"nickname": "Some_nick",
|
||||||
|
"channels": ["#channel", "#channel2"],
|
||||||
|
"geoip_database": "/app/geolite2-city.mmdb",
|
||||||
|
"geoip_asn": "/app/geolite2-asn.mmdb"
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
FROM debian:12-slim AS builder
|
||||||
|
|
||||||
|
ADD url /app/
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN apt update && apt upgrade -y && apt install musl musl-dev && \
|
||||||
|
git clone https://github.com/richfelker/musl-cross-make.git && \
|
||||||
|
cd musl-cross-make && make && make install && cd .. && \
|
||||||
|
CGO_ENABLED=1 CC=musl-gcc GOOS=linux GOARCH=amd64 go build -a -ldflags '-extldflags "-static"' -o irc_bot
|
||||||
|
|
||||||
|
FROM alpine:3.20
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=builder /app/irc_bot /app/
|
||||||
|
|
||||||
|
CMD ["irc_bot"]
|
|
@ -0,0 +1,12 @@
|
||||||
|
module irc_bot
|
||||||
|
|
||||||
|
go 1.23.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/oschwald/geoip2-golang v1.11.0 // indirect
|
||||||
|
github.com/oschwald/maxminddb-golang v1.13.0 // indirect
|
||||||
|
github.com/thoj/go-ircevent v0.0.0-20210723090443-73e444401d64 // indirect
|
||||||
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect
|
||||||
|
golang.org/x/sys v0.20.0 // indirect
|
||||||
|
golang.org/x/text v0.3.6 // indirect
|
||||||
|
)
|
|
@ -0,0 +1,16 @@
|
||||||
|
github.com/oschwald/geoip2-golang v1.11.0 h1:hNENhCn1Uyzhf9PTmquXENiWS6AlxAEnBII6r8krA3w=
|
||||||
|
github.com/oschwald/geoip2-golang v1.11.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo=
|
||||||
|
github.com/oschwald/maxminddb-golang v1.13.0 h1:R8xBorY71s84yO06NgTmQvqvTvlS/bnYZrrWX1MElnU=
|
||||||
|
github.com/oschwald/maxminddb-golang v1.13.0/go.mod h1:BU0z8BfFVhi1LQaonTwwGQlsHUEu9pWNdMfmq4ztm0o=
|
||||||
|
github.com/thoj/go-ircevent v0.0.0-20210723090443-73e444401d64 h1:l/T7dYuJEQZOwVOpjIXr1180aM9PZL/d1MnMVIxefX4=
|
||||||
|
github.com/thoj/go-ircevent v0.0.0-20210723090443-73e444401d64/go.mod h1:Q1NAJOuRdQCqN/VIWdnaaEhV8LpeO2rtlBP7/iDJNII=
|
||||||
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
|
||||||
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||||
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
@ -0,0 +1,153 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"crypto/tls"
|
||||||
|
|
||||||
|
"github.com/oschwald/geoip2-golang"
|
||||||
|
"github.com/thoj/go-ircevent"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
config, err := LoadConfig("config.json")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error loading config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
irccon := irc.IRC(config.Nickname, config.Nickname)
|
||||||
|
irccon.VerboseCallbackHandler = true
|
||||||
|
irccon.Debug = true
|
||||||
|
irccon.UseTLS = config.SSL
|
||||||
|
irccon.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||||
|
|
||||||
|
err = irccon.Connect(fmt.Sprintf("%s:%d", config.IRCServer, config.IRCPort))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to connect to IRC server: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
irccon.AddCallback("001", func(e *irc.Event) {
|
||||||
|
for _, channel := range config.Channels {
|
||||||
|
irccon.Join(channel)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
irccon.AddCallback("PRIVMSG", func(e *irc.Event) {
|
||||||
|
if len(e.Arguments) < 2 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
message := e.Arguments[1]
|
||||||
|
parts := strings.Fields(message)
|
||||||
|
if len(parts) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := strings.TrimPrefix(parts[0], "!")
|
||||||
|
if commandFunc, ok := commands[cmd]; ok {
|
||||||
|
commandFunc(irccon, e, parts)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
irccon.Loop()
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommandFunc func(irccon *irc.Connection, e *irc.Event, args []string)
|
||||||
|
|
||||||
|
var commands = map[string]CommandFunc{
|
||||||
|
"geoip": handleGeoIPCommand,
|
||||||
|
// add more commands here
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleGeoIPCommand(irccon *irc.Connection, e *irc.Event, args []string) {
|
||||||
|
if len(args) < 2 {
|
||||||
|
irccon.Privmsg(e.Arguments[0], "Usage: !geoip <IP or domain>")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ipOrDomain := args[1]
|
||||||
|
|
||||||
|
config, err := LoadConfig("config.json")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error loading config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cityDbPath := config.GeoIPDatabase // Update with actual path
|
||||||
|
asnDbPath := config.GeoIPASN // Update with actual path
|
||||||
|
|
||||||
|
ip := net.ParseIP(ipOrDomain)
|
||||||
|
if ip == nil {
|
||||||
|
resolvedIP, err := net.LookupIP(ipOrDomain)
|
||||||
|
if err != nil || len(resolvedIP) == 0 {
|
||||||
|
irccon.Privmsg(e.Arguments[0], fmt.Sprintf("Invalid IP address or domain: %s", ipOrDomain))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ip = resolvedIP[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
city = "N/A"
|
||||||
|
subdivision = "N/A"
|
||||||
|
country = "N/A"
|
||||||
|
latitude = "N/A"
|
||||||
|
longitude = "N/A"
|
||||||
|
asn = "N/A"
|
||||||
|
isp = "N/A"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Lookup City Information
|
||||||
|
if cityDb, err := geoip2.Open(cityDbPath); err == nil {
|
||||||
|
defer cityDb.Close()
|
||||||
|
|
||||||
|
if cityRecord, err := cityDb.City(ip); err == nil {
|
||||||
|
if name := cityRecord.City.Names["en"]; name != "" {
|
||||||
|
city = name
|
||||||
|
}
|
||||||
|
if len(cityRecord.Subdivisions) > 0 {
|
||||||
|
if name := cityRecord.Subdivisions[0].Names["en"]; name != "" {
|
||||||
|
subdivision = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if name := cityRecord.Country.Names["en"]; name != "" {
|
||||||
|
country = name
|
||||||
|
}
|
||||||
|
if lat := cityRecord.Location.Latitude; lat != 0 {
|
||||||
|
latitude = fmt.Sprintf("%.6f", lat)
|
||||||
|
}
|
||||||
|
if lon := cityRecord.Location.Longitude; lon != 0 {
|
||||||
|
longitude = fmt.Sprintf("%.6f", lon)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Printf("City lookup error: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Printf("Error opening City database: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup ASN Information
|
||||||
|
if asnDb, err := geoip2.Open(asnDbPath); err == nil {
|
||||||
|
defer asnDb.Close()
|
||||||
|
|
||||||
|
if asnRecord, err := asnDb.ASN(ip); err == nil {
|
||||||
|
if asnNum := asnRecord.AutonomousSystemNumber; asnNum != 0 {
|
||||||
|
asn = fmt.Sprintf("%d", asnNum)
|
||||||
|
}
|
||||||
|
if org := asnRecord.AutonomousSystemOrganization; org != "" {
|
||||||
|
isp = org
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Printf("ASN lookup error: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Printf("Error opening ASN database: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := fmt.Sprintf(
|
||||||
|
"IP: %s | Location: %s, %s, %s | Coordinates: %s, %s | ASN: %s | ISP: %s",
|
||||||
|
ip.String(), city, subdivision, country, latitude, longitude, asn, isp,
|
||||||
|
)
|
||||||
|
|
||||||
|
irccon.Privmsg(e.Arguments[0], response)
|
||||||
|
}
|
Loading…
Reference in New Issue