Move most configurations to a dedicated config file (using CUElang). Also added birthday notifier.
This commit is contained in:
parent
f7924261de
commit
59da01b0e5
5
.gitignore
vendored
5
.gitignore
vendored
@ -25,3 +25,8 @@ go.work.sum
|
||||
# env file
|
||||
.env
|
||||
|
||||
# config files
|
||||
*.cue
|
||||
|
||||
# database
|
||||
data.db
|
||||
|
17
go.mod
17
go.mod
@ -3,19 +3,28 @@ module dev.justinjudd.com/discord_bots
|
||||
go 1.24.1
|
||||
|
||||
require (
|
||||
github.com/bwmarrin/discordgo v0.29.0 // indirect
|
||||
cuelang.org/go v0.13.2
|
||||
github.com/bwmarrin/discordgo v0.29.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
modernc.org/sqlite v1.38.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.65.10 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
modernc.org/sqlite v1.38.0 // indirect
|
||||
)
|
||||
|
69
go.sum
69
go.sum
@ -1,37 +1,102 @@
|
||||
cuelabs.dev/go/oci/ociregistry v0.0.0-20250304105642-27e071d2c9b1 h1:Dmbd5Q+ENb2C6carvwrMsrOUwJ9X9qfL5JdW32gYAHo=
|
||||
cuelabs.dev/go/oci/ociregistry v0.0.0-20250304105642-27e071d2c9b1/go.mod h1:dqrnoZx62xbOZr11giMPrWbhlaV8euHwciXZEy3baT8=
|
||||
cuelang.org/go v0.13.2 h1:SagzeEASX4E2FQnRbItsqa33sSelrJjQByLqH9uZCE8=
|
||||
cuelang.org/go v0.13.2/go.mod h1:8MoQXu+RcXsa2s9mebJN1HJ1orVDc9aI9/yKi6Dzsi4=
|
||||
github.com/bwmarrin/discordgo v0.29.0 h1:FmWeXFaKUwrcL3Cx65c20bTRW+vOb6k8AnaP+EgjDno=
|
||||
github.com/bwmarrin/discordgo v0.29.0/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
|
||||
github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg=
|
||||
github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/emicklei/proto v1.14.0 h1:WYxC0OrBuuC+FUCTZvb8+fzEHdZMwLEF+OnVfZA3LXU=
|
||||
github.com/emicklei/proto v1.14.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
|
||||
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
|
||||
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
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.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
|
||||
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
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/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/protocolbuffers/txtpbfmt v0.0.0-20250129171521-feedd8250727 h1:A8EM8fVuYc0qbVMw9D6EiKdKTIm1SmLvAWcCc2mipGY=
|
||||
github.com/protocolbuffers/txtpbfmt v0.0.0-20250129171521-feedd8250727/go.mod h1:VmWrOlMnBZNtToCWzRlZlIXcJqjo0hS5dwQbRD62gL8=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
|
||||
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=
|
||||
modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
|
||||
modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
|
||||
modernc.org/fileutil v1.3.3 h1:3qaU+7f7xxTUmvU1pJTZiDLAIoJVdUSSauJNHg9yXoA=
|
||||
modernc.org/fileutil v1.3.3/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/libc v1.65.10 h1:ZwEk8+jhW7qBjHIT+wd0d9VjitRyQef9BnzlzGwMODc=
|
||||
modernc.org/libc v1.65.10/go.mod h1:StFvYpx7i/mXtBAfVOjaU0PWZOvIRoZSgXhrwXzr8Po=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.38.0 h1:+4OrfPQ8pxHKuWG4md1JpR/EYAh3Md7TdejuuzE7EUI=
|
||||
modernc.org/sqlite v1.38.0/go.mod h1:1Bj+yES4SVvBZ4cBOpVZ6QgesMCKpJZDq0nxYzOpmNE=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
|
225
main.go
225
main.go
@ -4,50 +4,98 @@ import (
|
||||
"database/sql"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"math/rand/v2"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/cuecontext"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/joho/godotenv"
|
||||
|
||||
_ "embed"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
var botToken string
|
||||
var announceChannelName string
|
||||
var cleanDelay = 30
|
||||
|
||||
var confFile string
|
||||
var dbFile string
|
||||
var dataPath string
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&confFile, "conf", ".env", ".env file w/ config variables")
|
||||
flag.StringVar(&dbFile, "db", "data.db", "db to store logs of events in")
|
||||
flag.StringVar(&dataPath, "data", "./", "file path for storing data")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
//go:embed schema.cue
|
||||
var schemaFile string
|
||||
|
||||
type Config struct {
|
||||
Server string
|
||||
VoiceChatAnnounce struct {
|
||||
Enable bool
|
||||
AnnounceChannel string
|
||||
JoinMessages []string
|
||||
CleanUpDelay int
|
||||
}
|
||||
BirthdayAnnounce struct {
|
||||
Enable bool
|
||||
AnnounceChannel string
|
||||
Birthdays []Birthday
|
||||
}
|
||||
}
|
||||
|
||||
func (c Config) String() string {
|
||||
s := strings.Builder{}
|
||||
s.WriteString(fmt.Sprintf("Guild ID: %s\n", c.Server))
|
||||
if c.VoiceChatAnnounce.Enable {
|
||||
s.WriteString("\tVoice Chat Accounce: ✅ \n")
|
||||
s.WriteString(fmt.Sprintf("\t\tTo Channel: %s\n", c.VoiceChatAnnounce.AnnounceChannel))
|
||||
s.WriteString(fmt.Sprintf("\t\t %d Custom messages\n", len(c.VoiceChatAnnounce.JoinMessages)))
|
||||
} else {
|
||||
s.WriteString("\tVoice Chat Accounce: ❌ \n")
|
||||
}
|
||||
if c.BirthdayAnnounce.Enable {
|
||||
s.WriteString("\tBirthday Accounce: ✅ \n")
|
||||
s.WriteString(fmt.Sprintf("\t\tTo Channel: %s\n", c.BirthdayAnnounce.AnnounceChannel))
|
||||
s.WriteString(fmt.Sprintf("\t\t %d Birthdays to Announce\n", len(c.BirthdayAnnounce.Birthdays)))
|
||||
} else {
|
||||
s.WriteString("\tBirthday Accounce: ❌ \n")
|
||||
}
|
||||
return s.String()
|
||||
}
|
||||
|
||||
type Birthday struct {
|
||||
Name string //Optional
|
||||
Member string
|
||||
Date string // Format MM/DD
|
||||
server string
|
||||
}
|
||||
|
||||
var joinMessages = []string{
|
||||
"<@%s> has joined <#%s>",
|
||||
"<@%s> has joined <#%s>; Join in for some nerd talk",
|
||||
"<#%[2]s> is the place to be! <@%[1]s> just joined",
|
||||
"<#%[2]s> just got a bit cooler, <@%[1]s> is now in",
|
||||
"<@%s> is hanging out in <#%s>",
|
||||
}
|
||||
|
||||
type Channel struct {
|
||||
GuildID, ChannelID string
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
*sql.DB
|
||||
configs map[string]Config
|
||||
}
|
||||
|
||||
func NewServer(dbFile string) (*Server, error) {
|
||||
db, err := sql.Open("sqlite", dbFile+"?_time_format=sqlite")
|
||||
db, err := sql.Open("sqlite", filepath.Join(dataPath, dbFile)+"?_time_format=sqlite")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to open db %q: %w", dbFile, err)
|
||||
}
|
||||
@ -70,28 +118,74 @@ func NewServer(dbFile string) (*Server, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create table: %w", err)
|
||||
}
|
||||
s := Server{DB: db}
|
||||
s := Server{DB: db, configs: map[string]Config{}}
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
func (s *Server) AddConfig(c Config) {
|
||||
s.configs[c.Server] = c
|
||||
log.Printf("Added server: \n%s\n", c)
|
||||
}
|
||||
|
||||
func main() {
|
||||
envs, err := godotenv.Read(confFile)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to get environment variables: %v", err)
|
||||
}
|
||||
s, err := NewServer(dbFile)
|
||||
if err != nil {
|
||||
log.Fatalf("can't create server: %v", err)
|
||||
}
|
||||
|
||||
ctx := cuecontext.New()
|
||||
schema := ctx.CompileString(schemaFile)
|
||||
|
||||
vfs := os.DirFS(dataPath)
|
||||
err = fs.WalkDir(vfs, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if filepath.Ext(d.Name()) != ".cue" {
|
||||
return nil
|
||||
}
|
||||
if d.Name() == "schema.cue" {
|
||||
return nil
|
||||
}
|
||||
f, err := vfs.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conf := ctx.CompileBytes(data, cue.Scope(schema))
|
||||
if err := conf.Validate(); err != nil {
|
||||
return fmt.Errorf("unable to validate config %q: %w", path, err)
|
||||
}
|
||||
cfg := Config{}
|
||||
fields, err := conf.Fields()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fields.Next()
|
||||
if err := fields.Value().Decode(&cfg); err != nil {
|
||||
return fmt.Errorf("unable to decode config %q: %w", path, err)
|
||||
}
|
||||
s.AddConfig(cfg)
|
||||
fmt.Printf("Loaded conf from %q\n", path)
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Error(s) loading conf files: %v", err)
|
||||
}
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
envs, err := godotenv.Read(filepath.Join(dataPath, confFile))
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to get environment variables: %v", err)
|
||||
}
|
||||
|
||||
// set values from env
|
||||
botToken = envs["BOT_TOKEN"]
|
||||
announceChannelName = envs["ANNOUNCE_CHANNEL"]
|
||||
if delay, ok := envs["CLEAN_DELAY"]; ok {
|
||||
if d2, err := strconv.Atoi(delay); err == nil {
|
||||
cleanDelay = d2
|
||||
}
|
||||
}
|
||||
|
||||
ds, err := discordgo.New("Bot " + botToken)
|
||||
if err != nil {
|
||||
@ -116,6 +210,10 @@ func main() {
|
||||
}
|
||||
defer ds.Close()
|
||||
|
||||
if err = s.setupBirthdayWatch(ds); err != nil {
|
||||
log.Fatalf("can't setup birthday watcher: %v", err)
|
||||
}
|
||||
|
||||
stop := make(chan os.Signal, 1)
|
||||
signal.Notify(stop, os.Interrupt)
|
||||
<-stop
|
||||
@ -135,6 +233,11 @@ func (s VoiceState) String() string {
|
||||
|
||||
// Handle for when someone joins a voice channel - send notification about them joining.
|
||||
func (s *Server) voiceStatus(ds *discordgo.Session, m *discordgo.VoiceStateUpdate) {
|
||||
guild, _ := ds.Guild(m.VoiceState.GuildID)
|
||||
server := s.configs[guild.ID]
|
||||
if !server.VoiceChatAnnounce.Enable {
|
||||
return
|
||||
}
|
||||
var state VoiceState
|
||||
switch {
|
||||
case len(m.ChannelID) == 0:
|
||||
@ -147,8 +250,6 @@ func (s *Server) voiceStatus(ds *discordgo.Session, m *discordgo.VoiceStateUpdat
|
||||
state = Switching
|
||||
}
|
||||
|
||||
guild, _ := ds.Guild(m.VoiceState.GuildID)
|
||||
|
||||
switch state {
|
||||
case Leaving, Switching:
|
||||
channel, _ := ds.Channel(m.BeforeUpdate.ChannelID)
|
||||
@ -176,33 +277,85 @@ func (s *Server) voiceStatus(ds *discordgo.Session, m *discordgo.VoiceStateUpdat
|
||||
return
|
||||
}
|
||||
|
||||
channels, err := ds.GuildChannels(m.GuildID)
|
||||
ch, err := ds.Channel(server.VoiceChatAnnounce.AnnounceChannel)
|
||||
if err != nil {
|
||||
log.Printf("Unable to get channels for Guild: %s", m.GuildID)
|
||||
return
|
||||
}
|
||||
var announceChannel *discordgo.Channel
|
||||
for _, c := range channels {
|
||||
if c.Name == announceChannelName {
|
||||
announceChannel = c
|
||||
}
|
||||
}
|
||||
if announceChannel == nil {
|
||||
log.Printf("Unable to get announce channel for Guild: %s", m.GuildID)
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf(joinMessages[rand.IntN(len(joinMessages))], m.UserID, m.ChannelID)
|
||||
messages := append(joinMessages, server.VoiceChatAnnounce.JoinMessages...)
|
||||
|
||||
msg := fmt.Sprintf(messages[rand.IntN(len(messages))], m.UserID, m.ChannelID)
|
||||
switch state {
|
||||
case Switching:
|
||||
msg = fmt.Sprintf("<@%s> has left <#%s> to join <#%s>", m.UserID, m.BeforeUpdate.ChannelID, m.ChannelID)
|
||||
}
|
||||
|
||||
sent, err := ds.ChannelMessageSend(announceChannel.ID, msg)
|
||||
sent, err := ds.ChannelMessageSend(ch.ID, msg)
|
||||
if err != nil {
|
||||
log.Printf("unable to send message: %s", err)
|
||||
}
|
||||
|
||||
time.AfterFunc(time.Second*time.Duration(cleanDelay), func() {
|
||||
time.AfterFunc(time.Second*time.Duration(server.VoiceChatAnnounce.CleanUpDelay), func() {
|
||||
ds.ChannelMessageDelete(sent.ChannelID, sent.ID)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) setupBirthdayWatch(ds *discordgo.Session) error {
|
||||
birthdays := map[string][]Birthday{} // Use a list in case there are collisions (See birthday paradox)
|
||||
for _, guild := range s.configs {
|
||||
if !guild.BirthdayAnnounce.Enable {
|
||||
continue
|
||||
}
|
||||
for _, b := range guild.BirthdayAnnounce.Birthdays {
|
||||
b.server = guild.Server
|
||||
birthdays[b.Date] = append(birthdays[b.Date], b)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
targetHour := 7
|
||||
targetMinute := 35
|
||||
now := time.Now()
|
||||
location, err := time.LoadLocation("America/Los_Angeles")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get timezone data: %w", err)
|
||||
}
|
||||
targetTime := time.Date(now.Year(), now.Month(), now.Day(), targetHour, targetMinute, 0, 0, location)
|
||||
if now.After(targetTime) {
|
||||
targetTime = targetTime.Add(24 * time.Hour)
|
||||
}
|
||||
initialDelay := targetTime.Sub(now)
|
||||
log.Printf("Waiting for %s for starting birthday watcher", initialDelay)
|
||||
birthdayMatcher := func(ds *discordgo.Session) {
|
||||
dateString := time.Now().Format("01/02")
|
||||
log.Printf("Looking for matching birthdays on %s", dateString)
|
||||
if bd, ok := birthdays[dateString]; ok {
|
||||
for _, b := range bd {
|
||||
log.Printf("It is %q's birthday today (%s)", b.Member, b.Date)
|
||||
if err := announceBirthday(ds, b.server, s.configs[b.server].BirthdayAnnounce.AnnounceChannel, b.Member); err != nil {
|
||||
log.Printf("Error w/ announcing: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
time.AfterFunc(initialDelay, func() {
|
||||
|
||||
log.Printf("Birthday watcher initiated")
|
||||
// Run now, but also set up a daily job
|
||||
birthdayMatcher(ds)
|
||||
c := time.Tick(24 * time.Hour)
|
||||
for range c {
|
||||
birthdayMatcher(ds)
|
||||
}
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func announceBirthday(ds *discordgo.Session, guildID, channelID, userId string) error {
|
||||
_, err := ds.ChannelMessageSend(channelID, fmt.Sprintf("Happy Birthday to <@%s>!!", userId))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to send message: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
31
schema.cue
Normal file
31
schema.cue
Normal file
@ -0,0 +1,31 @@
|
||||
// Initially generated by cue get go.
|
||||
|
||||
//cue:generate cue get go dev.justinjudd.com/discord_bots
|
||||
|
||||
package main
|
||||
|
||||
#Config: {
|
||||
Server: string
|
||||
VoiceChatAnnounce?: {
|
||||
Enable: bool | *false
|
||||
AnnounceChannel: string | *""
|
||||
JoinMessages: [...string] @go(,[]string)
|
||||
CleanUpDelay: int | *30
|
||||
} @go(,struct{Enable bool; AnnounceChannel string; JoinMessages []string; CleanUpDelay int})
|
||||
BirthdayAnnounce?: {
|
||||
Enable: bool | *false
|
||||
AnnounceChannel: string | *""
|
||||
Birthdays: [...#Birthday] @go(,[]Birthday)
|
||||
} @go(,struct{Enable bool; AnnounceChannel string; Birthdays []Birthday})
|
||||
}
|
||||
|
||||
#Birthday: {
|
||||
Name: string
|
||||
Member: string
|
||||
Date: string
|
||||
}
|
||||
|
||||
#Enabled: {
|
||||
Enable: true
|
||||
...
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user