Skip to content

Commit

Permalink
chore: Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
nidotls committed Oct 22, 2024
0 parents commit bfe9e5d
Show file tree
Hide file tree
Showing 8 changed files with 341 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
WORK_DIR=./.state/
WATCH_DOMAINS=example.com
NOTIFY_DISCORD_WEBHOOK=https://discord.com/api/webhooks/...
NOTIFY_DISCORD_USER_ID=
26 changes: 26 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/"
target-branch: "develop"
open-pull-requests-limit: 20
schedule:
interval: "daily"
time: "09:00"
timezone: "Europe/Berlin"
- package-ecosystem: "docker"
directory: "/"
target-branch: "develop"
open-pull-requests-limit: 20
schedule:
interval: "daily"
time: "09:00"
timezone: "Europe/Berlin"
- package-ecosystem: "github-actions"
directory: "/"
target-branch: "develop"
open-pull-requests-limit: 20
schedule:
interval: "daily"
time: "09:00"
timezone: "Europe/Berlin"
43 changes: 43 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Container Publish

on:
push:
branches:
- "*"
tags:
- "*"

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Docker meta
id: meta
uses: docker/metadata-action@v3
with:
images: ghcr.io/${{ github.repository_owner }}/whois-watcher
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}},enable=${{ !startsWith(github.ref, 'refs/tags/0.0.') }}
type=semver,pattern={{major}},enable=${{ !startsWith(github.ref, 'refs/tags/0.') }}
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ github.token }}

- name: Build and push
uses: docker/build-push-action@v2
with:
context: ./
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
51 changes: 51 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Created by https://www.toptal.com/developers/gitignore/api/go,visualstudiocode,dotenv
# Edit at https://www.toptal.com/developers/gitignore?templates=go,visualstudiocode,dotenv

### dotenv ###
.env

### Go ###
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

# Go workspace file
go.work

### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets

# Local History for Visual Studio Code
.history/

# Built Visual Studio Code Extensions
*.vsix

### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide

# End of https://www.toptal.com/developers/gitignore/api/go,visualstudiocode,dotenv

.state/
22 changes: 22 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Stage 1: Modules caching
FROM golang:1.23 as modules
COPY go.mod go.sum /modules/
WORKDIR /modules
RUN go mod download

# Stage 2: Build
FROM golang:1.23 as builder
COPY --from=modules /go/pkg /go/pkg
COPY . /workdir
WORKDIR /workdir

# Build your app
RUN GOOS=linux GOARCH=amd64 go build -o /bin/whois-watcher

# Stage 3: Final
FROM ubuntu:jammy
COPY --from=builder /go/bin/playwright /bin/whois-watcher /
RUN apt-get update && apt-get install -y ca-certificates tzdata \
&& rm -rf /var/lib/apt/lists/*

CMD ["/whois-watcher"]
13 changes: 13 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module github.com/nidotls/whois-watcher

go 1.23.2

require github.com/sirupsen/logrus v1.9.3

require (
github.com/gtuk/discordwebhook v1.2.0 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/likexian/whois v1.15.5 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/sys v0.26.0 // indirect
)
29 changes: 29 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gtuk/discordwebhook v1.2.0 h1:7+gWPKSGyXjopu/6X9+oGbn0knTkDVXUM909+IXGZ/U=
github.com/gtuk/discordwebhook v1.2.0/go.mod h1:U3LdXNJ1e0bx3MMe2a4mB1VBantPHOPly2jNd8ZWXec=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/likexian/whois v1.15.5 h1:gpPxyCTJtLtJDmakHCo//0ZjK/ocI01GCAd/WBJ2oH8=
github.com/likexian/whois v1.15.5/go.mod h1:4b6o1QTCfjwrB5I3KeNQnn79QtuPUTsewsE+ys94I78=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
153 changes: 153 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package main

import (
"errors"
"fmt"
"os"
"os/exec"
"path"
"strings"
"time"

"github.com/gtuk/discordwebhook"
"github.com/joho/godotenv"
"github.com/likexian/whois"
"github.com/sirupsen/logrus"
)

func main() {
_ = godotenv.Load()

workDir := os.Getenv("WORK_DIR")
if workDir == "" {
workDir = "/tmp/whois-watch"
}
domains := strings.Split(os.Getenv("WATCH_DOMAINS"), " ")

err := os.MkdirAll(workDir, 0755)
if err != nil {
logrus.Fatalf("create work dir error: %v", err)
}
err = os.MkdirAll(path.Join(workDir, "history"), 0755)
if err != nil {
logrus.Fatalf("create history work dir error: %v", err)
}

for {
run(workDir, domains)

time.Sleep(12 * time.Hour)
}
}

func run(workDir string, domains []string) {
for _, domain := range domains {
log := logrus.WithField("domain", domain)

result := whoisDomain(domain)

file, err := os.ReadFile(path.Join(workDir, fmt.Sprintf("%s.txt", domain)))
if err != nil {
if errors.Is(err, os.ErrNotExist) {
log.Infof("file not exist, create new file")
} else {
log.Errorf("read file error: %v", err)
}

err = os.WriteFile(path.Join(workDir, fmt.Sprintf("%s.txt", domain)), []byte(result), 0644)
if err != nil {
log.Errorf("write file error: %v", err)
continue
}
continue
}

err = os.WriteFile(path.Join(workDir, fmt.Sprintf("%s.txt", domain)), []byte(result), 0644)
if err != nil {
log.Errorf("write file error: %v", err)
continue
}

diff, err := diffLineByLine(string(file), result)
if err != nil {
log.Errorf("diff error: %v", err)
continue
}

if len(diff) == 0 {
log.Infof("whois result is same as before")
continue
}

log.Infof("whois result is different from before:")
for _, line := range strings.Split(diff, "\n") {
log.Infof(" %s", line)
}

content := "# Domain `" + domain + "` has been changed"
if os.Getenv("NOTIFY_DISCORD_USER_ID") != "" {
content += "\n\n<@" + os.Getenv("NOTIFY_DISCORD_USER_ID") + ">"
}
content += "\n\n```diff\n" + diff + "```"
message := discordwebhook.Message{
Content: &content,
}

err = discordwebhook.SendMessage(os.Getenv("NOTIFY_DISCORD_WEBHOOK"), message)
if err != nil {
log.Fatal(err)
}

err = os.WriteFile(path.Join(workDir, "history", fmt.Sprintf("%s.%d.diff", domain, time.Now().Unix())), []byte(diff), 0644)
if err != nil {
log.Errorf("write diff file error: %v", err)
continue
}
}
}

func diffLineByLine(from, to string) (string, error) {
workDir, err := os.MkdirTemp("", "diff")
defer os.RemoveAll(workDir)
if err != nil {
return "", err
}

fromFile := path.Join(workDir, "from.txt")
toFile := path.Join(workDir, "to.txt")

err = os.WriteFile(fromFile, []byte(from), 0644)
if err != nil {
return "", err
}
err = os.WriteFile(toFile, []byte(to), 0644)
if err != nil {
return "", err
}

cmd := exec.Command("diff", fromFile, toFile)
out, err := cmd.Output()
if err != nil && cmd.ProcessState.ExitCode() != 1 {
return "", err
}

return string(out), nil
}

func whoisDomain(domain string) string {
result, err := whois.Whois(domain)
if err != nil {
return err.Error()
}

lines := strings.Split(result, "\n")
result = ""
for _, line := range lines {
if strings.HasPrefix(line, "%") || strings.HasPrefix(line, ">>>") {
continue
}
result += line + "\n"
}

return result
}

0 comments on commit bfe9e5d

Please sign in to comment.