mirror of
https://github.com/watn3y/steamsalty.git
synced 2025-04-20 00:11:23 +02:00
v0.1
This commit is contained in:
parent
6aa64b6287
commit
3ec330e3a2
16 changed files with 408 additions and 1 deletions
38
.github/workflows/docker-on-push.yml
vendored
Normal file
38
.github/workflows/docker-on-push.yml
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
name: Build and Push to Docker Hub on changes to master branch
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{github.actor}}
|
||||
password: ${{secrets.GHCR_TOKEN}} # needs read:packages, write:packages. delete:packages
|
||||
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
ghcr.io/${{ github.repository }}:master
|
||||
ghcr.io/${{ github.repository }}:commit-${{ github.sha }}
|
||||
|
||||
|
16
Dockerfile
Normal file
16
Dockerfile
Normal file
|
@ -0,0 +1,16 @@
|
|||
FROM golang:alpine AS builder
|
||||
|
||||
WORKDIR /tmp/build
|
||||
|
||||
COPY . .
|
||||
RUN go mod download
|
||||
RUN go build -o /tmp/build/bin/steamsalty
|
||||
|
||||
|
||||
|
||||
FROM scratch
|
||||
WORKDIR /app
|
||||
COPY --from=builder /tmp/build/bin/steamsalty /app/steamsalty
|
||||
|
||||
|
||||
ENTRYPOINT ["/app/steamsalty"]
|
|
@ -1,2 +1,2 @@
|
|||
# steamsalty
|
||||
A bot that notifies you about new Steam Profile comments on telegram
|
||||
Get notifications about Steam Comments on Telegram
|
||||
|
|
10
bot.go
Normal file
10
bot.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"watn3y/steamsalty/botIO"
|
||||
"watn3y/steamsalty/steam"
|
||||
)
|
||||
|
||||
func bot() {
|
||||
steam.StartWatchers(botIO.Authenticate())
|
||||
}
|
20
botIO/authenticate.go
Normal file
20
botIO/authenticate.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
package botIO
|
||||
|
||||
import (
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"github.com/rs/zerolog/log"
|
||||
"watn3y/steamsalty/config"
|
||||
)
|
||||
|
||||
func Authenticate() *tgbotapi.BotAPI {
|
||||
bot, err := tgbotapi.NewBotAPI(config.BotConfig.TelegramAPIToken)
|
||||
if err != nil {
|
||||
log.Panic().Err(err).Msg("Failed to authenticate")
|
||||
}
|
||||
|
||||
bot.Debug = config.BotConfig.DebugMode
|
||||
|
||||
log.Info().Int64("ID", bot.Self.ID).Str("username", bot.Self.UserName).Msg("Successfully authenticated to Telegram API")
|
||||
|
||||
return bot
|
||||
}
|
71
botIO/sending.go
Normal file
71
botIO/sending.go
Normal file
|
@ -0,0 +1,71 @@
|
|||
package botIO
|
||||
|
||||
import (
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func SendMessage(message tgbotapi.MessageConfig, bot *tgbotapi.BotAPI) (result tgbotapi.Message) {
|
||||
result, err := bot.Send(message)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to send message")
|
||||
return
|
||||
}
|
||||
|
||||
log.Info().Int64("chat", result.Chat.ID).Str("msg", result.Text).Msg("Sent message")
|
||||
log.Debug().Interface("msg", result).Msg("")
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func EditMessage(message tgbotapi.EditMessageTextConfig, bot *tgbotapi.BotAPI) (result tgbotapi.Message) {
|
||||
result, err := bot.Send(message)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to edit message")
|
||||
return
|
||||
}
|
||||
|
||||
log.Info().Int64("chat", result.Chat.ID).Str("msg", result.Text).Msg("Edited message")
|
||||
log.Debug().Interface("msg", result).Msg("")
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func SendVideo(message tgbotapi.VideoConfig, bot *tgbotapi.BotAPI) (result tgbotapi.Message) {
|
||||
result, err := bot.Send(message)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to send video")
|
||||
return
|
||||
}
|
||||
|
||||
log.Info().Int64("chat", result.Chat.ID).Msg("Sent video")
|
||||
log.Debug().Interface("video", result).Msg("")
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func SendPhoto(message tgbotapi.PhotoConfig, bot *tgbotapi.BotAPI) (result tgbotapi.Message) {
|
||||
result, err := bot.Send(message)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to send photo")
|
||||
return
|
||||
}
|
||||
|
||||
log.Info().Int64("chat", result.Chat.ID).Msg("Sent photo")
|
||||
log.Debug().Interface("photo", result).Msg("")
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func SendSticker(message tgbotapi.StickerConfig, bot *tgbotapi.BotAPI) (result tgbotapi.Message) {
|
||||
result, err := bot.Send(message)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to send sticker")
|
||||
return
|
||||
}
|
||||
|
||||
log.Info().Int64("chat", result.Chat.ID).Msg("Sent sticker")
|
||||
log.Debug().Interface("sticker", result).Msg("")
|
||||
|
||||
return result
|
||||
}
|
24
config/config.go
Normal file
24
config/config.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
envconfig "github.com/sethvargo/go-envconfig"
|
||||
)
|
||||
|
||||
var BotConfig config
|
||||
|
||||
func LoadConfig() {
|
||||
if err := envconfig.Process(context.Background(), &BotConfig); err != nil {
|
||||
log.Panic().Err(err).Msg("error parsing config from env variables")
|
||||
}
|
||||
|
||||
if !BotConfig.DebugMode {
|
||||
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
||||
}
|
||||
|
||||
log.Info().Msg("Loaded config")
|
||||
log.Debug().Interface("config", BotConfig).Msg("")
|
||||
|
||||
}
|
9
config/types.go
Normal file
9
config/types.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package config
|
||||
|
||||
type config struct {
|
||||
TelegramAPIToken string `env:"TELEGRAMAPITOKEN, required"`
|
||||
SteamAPIKey string `env:"STEAMAPIKEY, required"`
|
||||
DebugMode bool `env:"DEBUGMODE, default=false"`
|
||||
ChatID int64 `env:"CHATID"`
|
||||
Watchers []uint64 `env:"WATCHERS"`
|
||||
}
|
11
docker-compose.yaml
Normal file
11
docker-compose.yaml
Normal file
|
@ -0,0 +1,11 @@
|
|||
services:
|
||||
steamsalty:
|
||||
image: ghcr.io/watn3y/steamsalty:master
|
||||
container_name: steamsalty
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TELEGRAMAPITOKEN=
|
||||
- STEAMAPIKEY=
|
||||
- DebugMode=false
|
||||
- CHATID=123
|
||||
- WATCHERS=123,456,789
|
17
go.mod
Normal file
17
go.mod
Normal file
|
@ -0,0 +1,17 @@
|
|||
module watn3y/steamsalty
|
||||
|
||||
go 1.23.4
|
||||
|
||||
require (
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
|
||||
github.com/rs/zerolog v1.33.0
|
||||
github.com/sethvargo/go-envconfig v1.1.0
|
||||
github.com/Philipp15b/go-steamapi v0.0.0-20210114153316-ec4fdd23b4c1
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
)
|
25
go.sum
Normal file
25
go.sum
Normal file
|
@ -0,0 +1,25 @@
|
|||
github.com/Philipp15b/go-steamapi v0.0.0-20210114153316-ec4fdd23b4c1 h1:PD13eMe9XAgPQ0SYWyirqwyOJG90TlEWApCw8A699l0=
|
||||
github.com/Philipp15b/go-steamapi v0.0.0-20210114153316-ec4fdd23b4c1/go.mod h1:eQR7Xf64m2ALDAQE7Nr9ylFZhav1izvF3zzysKPhb0I=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/sethvargo/go-envconfig v1.1.0 h1:cWZiJxeTm7AlCvzGXrEXaSTCNgip5oJepekh/BOQuog=
|
||||
github.com/sethvargo/go-envconfig v1.1.0/go.mod h1:JLd0KFWQYzyENqnEPWWZ49i4vzZo/6nRidxI8YvGiHw=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
35
main.go
Normal file
35
main.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"watn3y/steamsalty/config"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("Starting SteamSalty...")
|
||||
|
||||
configureLogger()
|
||||
|
||||
config.LoadConfig()
|
||||
|
||||
bot()
|
||||
|
||||
}
|
||||
|
||||
func configureLogger() {
|
||||
output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.DateTime}
|
||||
|
||||
log.Logger = zerolog.New(output).With().Timestamp().Caller().Logger()
|
||||
|
||||
//! note that we overwrite the loglevel after loading the config in config/config.go. This is just the default
|
||||
zerolog.SetGlobalLevel(zerolog.TraceLevel)
|
||||
|
||||
log.Info().Msg("Started Logger")
|
||||
|
||||
}
|
17
steam/api.go
Normal file
17
steam/api.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package steam
|
||||
|
||||
import (
|
||||
"github.com/Philipp15b/go-steamapi"
|
||||
"github.com/rs/zerolog/log"
|
||||
"watn3y/steamsalty/config"
|
||||
)
|
||||
|
||||
func getPlayerDetails(steamID uint64) (summary steamapi.PlayerSummary) {
|
||||
|
||||
response, err := steamapi.GetPlayerSummaries([]uint64{steamID}, config.BotConfig.SteamAPIKey)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to get Player Summary")
|
||||
}
|
||||
|
||||
return response[0]
|
||||
}
|
48
steam/http.go
Normal file
48
steam/http.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
package steam
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/rs/zerolog/log"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func getComments(steamID uint64, start int, count int) (comments CommentResponse) {
|
||||
|
||||
baseURL := "https://steamcommunity.com/comment/Profile/render/"
|
||||
|
||||
url, err := url.Parse(baseURL + strconv.FormatUint(steamID, 10))
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Unable to Parse SteamID into URL")
|
||||
return
|
||||
}
|
||||
|
||||
query := url.Query()
|
||||
query.Set("start", strconv.Itoa(start))
|
||||
query.Set("count", strconv.Itoa(count))
|
||||
url.RawQuery = query.Encode()
|
||||
|
||||
resp, err := http.Get(url.String())
|
||||
if err != nil || resp.StatusCode != http.StatusOK {
|
||||
log.Error().Err(err).Int("Response Code", resp.StatusCode).Msg("Failed to get Comments")
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Read the response body
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to parse Comments")
|
||||
log.Trace().Interface("Body", resp.Body)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &comments)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to parse Comments as JSON")
|
||||
}
|
||||
|
||||
log.Debug().Interface("CommentPage", comments).Msg("Successfully got Comment Page")
|
||||
return comments
|
||||
}
|
57
steam/profile.go
Normal file
57
steam/profile.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package steam
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"github.com/rs/zerolog/log"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
"watn3y/steamsalty/botIO"
|
||||
"watn3y/steamsalty/config"
|
||||
)
|
||||
|
||||
var sleeptime time.Duration = 10 * time.Second
|
||||
|
||||
func StartWatchers(bot *tgbotapi.BotAPI) {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for _, steamID := range config.BotConfig.Watchers {
|
||||
wg.Add(1)
|
||||
go func(steamID uint64) {
|
||||
defer wg.Done()
|
||||
watcher(bot, steamID)
|
||||
}(steamID)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func watcher(bot *tgbotapi.BotAPI, steamID uint64) {
|
||||
log.Info().Uint64("SteamID", steamID).Msg("Started Watcher")
|
||||
var previousCount int
|
||||
for {
|
||||
currentCount := getComments(steamID, math.MaxInt32, 0).TotalCount
|
||||
if previousCount == 0 || currentCount <= previousCount {
|
||||
previousCount = currentCount
|
||||
time.Sleep(sleeptime)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Info().Int("NumComment", currentCount).Uint64("SteamID", steamID).Msg("Found new comment")
|
||||
|
||||
player := getPlayerDetails(steamID)
|
||||
|
||||
msg := tgbotapi.MessageConfig{
|
||||
BaseChat: tgbotapi.BaseChat{ChatID: config.BotConfig.ChatID},
|
||||
ParseMode: "html",
|
||||
DisableWebPagePreview: false,
|
||||
Text: fmt.Sprintf(`New comment on <a href="%s">%s's</a> profile`, player.ProfileURL, player.PersonaName),
|
||||
}
|
||||
|
||||
botIO.SendMessage(msg, bot)
|
||||
|
||||
previousCount = currentCount
|
||||
time.Sleep(sleeptime)
|
||||
}
|
||||
}
|
9
steam/types.go
Normal file
9
steam/types.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package steam
|
||||
|
||||
type CommentResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Start int `json:"start"`
|
||||
TotalCount int `json:"total_count"`
|
||||
CommentsHTML string `json:"comments_html"`
|
||||
Timelastpost int `json:"timelastpost"`
|
||||
}
|
Loading…
Reference in a new issue