Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Steve Lawton committed Sep 26, 2024
0 parents commit c075884
Show file tree
Hide file tree
Showing 7 changed files with 218 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
DISCORD_BOT_TOKEN=
DISCORD_CHANNEL_ID=
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/.env
14 changes: 14 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Build Stage
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o ip-monitor .

# Run Stage
FROM alpine:latest
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /root/
COPY --from=builder /app/ip-monitor .
CMD ["./ip-monitor"]
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# IP Monitor Discord Bot

This Go application monitors your external IP address and updates a pinned message in a specified Discord channel whenever the IP changes. It also sends a broadcast message notifying about the IP change.

## Table of Contents

- [Prerequisites](#prerequisites)
- [Setup Instructions](#setup-instructions)
- [1. Clone the Repository](#1-clone-the-repository)
- [2. Set Up Environment Variables](#2-set-up-environment-variables)
- [3. Build the Docker Image](#3-build-the-docker-image)
- [4. Run the Docker Container](#4-run-the-docker-container)
- [Environment Variables](#environment-variables)
- [Stopping the Bot](#stopping-the-bot)
- [Updating the Bot](#updating-the-bot)
- [Notes](#notes)
- [Troubleshooting](#troubleshooting)
- [License](#license)

## Prerequisites

- **Docker**: Ensure Docker is installed on your system.
- **Discord Bot**: You need a Discord bot token and the ID of the channel where the bot will operate.

## Setup Instructions

### 1. Clone the Repository

```bash
git clone https://github.com/yourusername/ip-monitor.git
cd ip-monitor
```

### 2. Create a Discord Bot

- Go to the [Discord Developer Portal](https://discord.com/developers/applications).
- Create a new application and add a bot to it.
- Copy the bot's token; you'll need it for authentication.
- Invite the bot to your server with appropriate permissions (send messages, manage messages).
10 changes: 10 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: '3'

services:
ip-monitor:
build: .
container_name: ip-monitor
restart: unless-stopped
environment:
- DISCORD_BOT_TOKEN=${DISCORD_BOT_TOKEN}
- DISCORD_CHANNEL_ID=${DISCORD_CHANNEL_ID}
14 changes: 14 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module ip-monitor

go 1.22

require (
github.com/bwmarrin/discordgo v0.28.1
github.com/joho/godotenv v1.5.1
)

require (
github.com/gorilla/websocket v1.5.3 // indirect
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
)
138 changes: 138 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package main

import (
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"

"github.com/bwmarrin/discordgo"
"github.com/joho/godotenv"
)

const (
ipCheckURL = "https://api.ipify.org" // Service to get external IP
checkInterval = 1 * time.Hour // Interval between IP checks
)

var (
previousIP string
)

func main() {
// Load environment variables from .env file
err := godotenv.Load()
if err != nil {
log.Println("No .env file found or error reading .env file")
}

// Now you can use os.Getenv to get the variables
botToken := os.Getenv("DISCORD_BOT_TOKEN")
channelID := os.Getenv("DISCORD_CHANNEL_ID")

if botToken == "" || channelID == "" {
log.Fatal("Bot token or channel ID not set. Please set DISCORD_BOT_TOKEN and DISCORD_CHANNEL_ID.")
}

// Create a new Discord session
dg, err := discordgo.New("Bot " + botToken)
if err != nil {
log.Fatalf("Error creating Discord session: %v", err)
}

// Open a WebSocket connection to Discord
err = dg.Open()
if err != nil {
log.Fatalf("Error opening connection to Discord: %v", err)
}
defer dg.Close()

log.Println("Bot is now running. Press CTRL+C to exit.")

// Handle graceful shutdown
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)

// Start the IP monitoring in a goroutine
go func() {
for {
currentIP, err := getExternalIP()
if err != nil {
log.Printf("Error fetching external IP: %v", err)
time.Sleep(checkInterval)
continue
}

if currentIP != previousIP {
log.Printf("IP changed from %s to %s", previousIP, currentIP)
err = updateDiscordMessage(dg, channelID, currentIP)
if err != nil {
log.Printf("Error updating Discord message: %v", err)
}
previousIP = currentIP
}

time.Sleep(checkInterval)
}
}()

// Wait for a termination signal
<-stop
log.Println("Shutting down bot.")
}

func getExternalIP() (string, error) {
resp, err := http.Get(ipCheckURL)
if err != nil {
return "", err
}
defer resp.Body.Close()

ip, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}

return string(ip), nil
}

func updateDiscordMessage(dg *discordgo.Session, channelID, currentIP string) error {
// Fetch pinned messages
pinnedMessages, err := dg.ChannelMessagesPinned(channelID)
if err != nil {
return fmt.Errorf("error fetching pinned messages: %w", err)
}

var pinnedMessageID string
if len(pinnedMessages) > 0 {
// Assume the first pinned message is the one to update
pinnedMessageID = pinnedMessages[0].ID
_, err = dg.ChannelMessageEdit(channelID, pinnedMessageID, fmt.Sprintf("Current IP Address: `%s`", currentIP))
if err != nil {
return fmt.Errorf("error editing pinned message: %w", err)
}
} else {
// Send a new message and pin it
msg, err := dg.ChannelMessageSend(channelID, fmt.Sprintf("Current IP Address: `%s`", currentIP))
if err != nil {
return fmt.Errorf("error sending message: %w", err)
}

err = dg.ChannelMessagePin(channelID, msg.ID)
if err != nil {
return fmt.Errorf("error pinning message: %w", err)
}
}

// Send a broadcast message
_, err = dg.ChannelMessageSend(channelID, fmt.Sprintf("IP Address has changed to `%s`", currentIP))
if err != nil {
return fmt.Errorf("error sending broadcast message: %w", err)
}

return nil
}

0 comments on commit c075884

Please sign in to comment.