removed index.md
This commit is contained in:
parent
9d8c9b5590
commit
050923e222
1 changed files with 0 additions and 516 deletions
516
docs/index.md
516
docs/index.md
|
@ -1,516 +0,0 @@
|
||||||
# GoWatch
|
|
||||||
[](https://drone.broodjeaap.net/broodjeaap/go-watch)
|
|
||||||
|
|
||||||
A change detection server that can notify through various services, written in Go
|
|
||||||
# Intro
|
|
||||||
|
|
||||||
GoWatch works through filters, a filter performs operations on the input it recieves.
|
|
||||||
Here is an example of a 'Watch' that calculates the lowest and average price of 4090s on NewEgg and notifies the user if the lowest price changed:
|
|
||||||

|
|
||||||
|
|
||||||
Note that everything, including scheduling/storing/notifying, is a `filter`.
|
|
||||||
|
|
||||||
`Schedule` is a [cron](#cron) filter with a '@every 15m' value, so this will run every 15 minutes.
|
|
||||||
|
|
||||||
`NewEgg Fetch` is a [Get URL](#get-url) filter with a 'https://www.newegg.com/p/pl?N=100007709&d=4090&isdeptsrh=1&PageSize=96' value, it's output will be the HTTP response.
|
|
||||||
|
|
||||||
`Select Price` is a [CSS](#css) filter with the value '.item-container .item-action strong[class!="item-buying-choices-price"]' value, it's output will be the html elements containing the prices.
|
|
||||||
An [XPath](#xpath) filter could also have been used.
|
|
||||||
|
|
||||||
`Sanitize` is a [Replace](#replace) filter, using a regular expression ('[^0-9]') it removes anything that's not a number.
|
|
||||||
|
|
||||||
`Avg` is an [Average](#average) filter, it calculates the average value of its inputs.
|
|
||||||
|
|
||||||
`Min` is a [Minimum](#minimum) filter, it calculates the minimum value of its inputs.
|
|
||||||
|
|
||||||
`Average` and `Minimum` are [Store](#store) filters, they store its input values in the database.
|
|
||||||
|
|
||||||
`Diff` is a [Different Than Last](#different-than-last) filter, only passing on the inputs that are different then the last value stored in the database.
|
|
||||||
|
|
||||||
`Notify` is a [Notify](#notify) filter, if there are any inputs to this filter, it will execute a template and send the result to a user defined 'notifier' (Telegram/Discord/etc).
|
|
||||||
# Run
|
|
||||||
|
|
||||||
## Binary
|
|
||||||
|
|
||||||
Download
|
|
||||||
|
|
||||||
## Docker
|
|
||||||
|
|
||||||
Easiest way to get started is with the prebuilt docker image `ghcr.io/broodjeaap/go-watch:latest`, first get a config template:
|
|
||||||
`docker run --rm ghcr.io/broodjeaap/go-watch:latest -printConfig 2> config.yaml`
|
|
||||||
|
|
||||||
Or:
|
|
||||||
`docker run --rm -v $PWD:/config ghcr.io/broodjeaap/go-watch:latest -writeConfig /config/config.yaml`
|
|
||||||
|
|
||||||
After modifying the config to fit your needs, start the docker container
|
|
||||||
```
|
|
||||||
docker run \
|
|
||||||
-p 8080:8080 \
|
|
||||||
-v $PWD/:/config \
|
|
||||||
ghcr.io/broodjeaap/go-watch:latest
|
|
||||||
```
|
|
||||||
### Database
|
|
||||||
|
|
||||||
By default, GoWatch will use an SQLite database, stored in the `/config` directory for the docker image, which is probably fine for most use cases.
|
|
||||||
|
|
||||||
But you can use another database by changing the `database.dsn` value in the config or `GOWATCH_DATABASE_DSN` environment variable, for example with a PostgreSQL database:
|
|
||||||
```
|
|
||||||
version: "3"
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
image: ghcr.io/broodjeaap/go-watch:latest
|
|
||||||
container_name: go-watch
|
|
||||||
environment:
|
|
||||||
- GOWATCH_DATABASE_DSN=postgres://gorm:gorm@db:5432/gorm
|
|
||||||
volumes:
|
|
||||||
- /host/path/to/config:/config
|
|
||||||
ports:
|
|
||||||
- "8080:8080"
|
|
||||||
depends_on:
|
|
||||||
db:
|
|
||||||
condition: service_healthy
|
|
||||||
db:
|
|
||||||
image: postgres:15
|
|
||||||
environment:
|
|
||||||
- POSTGRES_USER=gorm
|
|
||||||
- POSTGRES_PASSWORD=gorm
|
|
||||||
- POSTGRES_DB=gorm
|
|
||||||
volumes:
|
|
||||||
- /host/path/to/db:/var/lib/postgresql/data
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Pruning
|
|
||||||
|
|
||||||
An automatic database prune job that removes repeating values can be scheduled by adding a cron schedule to the config:
|
|
||||||
```
|
|
||||||
database:
|
|
||||||
dsn: "/config/watch.db"
|
|
||||||
prune: "@every 1h"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Proxy
|
|
||||||
|
|
||||||
GoWatch has some basic proxy support, using the config we can point GoWatch to a proxy server:
|
|
||||||
```
|
|
||||||
proxy:
|
|
||||||
proxy_url: http://proxy.com:1234
|
|
||||||
```
|
|
||||||
|
|
||||||
When using the docker image, the `HTTP_PROXY` and `HTTPS_PROXY` environment variables can also be used:
|
|
||||||
```
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
image: ghcr.io/broodjeaap/go-watch:latest
|
|
||||||
container_name: go-watch
|
|
||||||
environment:
|
|
||||||
- HTTP_PROXY=http://proxy.com:1234
|
|
||||||
- HTTPS_PROXY=http://proxy.com:1234
|
|
||||||
```
|
|
||||||
#### Proxy pools
|
|
||||||
|
|
||||||
Proxy 'pools' are not directly supported by GoWatch, but can still be set up by using a proxy, for example with [Squid](http://www.squid-cache.org/):
|
|
||||||
```
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
image: ghcr.io/broodjeaap/go-watch:latest
|
|
||||||
container_name: go-watch
|
|
||||||
environment:
|
|
||||||
- HTTP_PROXY=http://squid_proxy:3128
|
|
||||||
- HTTPS_PROXY=http://squid_proxy:3128
|
|
||||||
squid_proxy:
|
|
||||||
image: sameersbn/squid:latest
|
|
||||||
volumes:
|
|
||||||
- /path/to/squid.conf:/etc/squid/squid.conf
|
|
||||||
```
|
|
||||||
|
|
||||||
And in the `squid.conf` the proxy pool would be defined like this:
|
|
||||||
```
|
|
||||||
cache_peer proxy1.com parent 3128 0 round-robin no-query
|
|
||||||
cache_peer proxy2.com parent 3128 0 round-robin no-query login=user:pass
|
|
||||||
```
|
|
||||||
|
|
||||||
### Browserless
|
|
||||||
|
|
||||||
Some websites don't send all content on the first request, it's added later through javascript, Amazon does this for example.
|
|
||||||
To still be able to watch products from these websites, GoWatch supports [Browserless](https://www.browserless.io/), the Browserless URL can be added to the config:
|
|
||||||
```
|
|
||||||
browserless:
|
|
||||||
url: http://your.browserless:3000/content
|
|
||||||
```
|
|
||||||
|
|
||||||
Or as an environment variable, for example in a docker-compose:
|
|
||||||
```
|
|
||||||
version: "3"
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
image: ghcr.io/broodjeaap/go-watch:latest
|
|
||||||
container_name: go-watch
|
|
||||||
environment:
|
|
||||||
- GOWATCH_BROWSERLESS_URL=http://browserless:3000/content
|
|
||||||
volumes:
|
|
||||||
- /host/path/to/config:/config
|
|
||||||
ports:
|
|
||||||
- "8080:8080"
|
|
||||||
browserless:
|
|
||||||
image: browserless/chrome:latest
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that the proxy environment variables can be added to the Browserless container to still allow for proxying.
|
|
||||||
|
|
||||||
### Authentication
|
|
||||||
|
|
||||||
GoWatch doesn't have built in authentication, but we can use a reverse proxy for that, for example through Traefik:
|
|
||||||
```
|
|
||||||
version: "3"
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
image: ghcr.io/broodjeaap/go-watch:latest
|
|
||||||
container_name: go-watch
|
|
||||||
environment:
|
|
||||||
- GOWATCH_DATABASE_DSN=postgres://gorm:gorm@db:5432/gorm
|
|
||||||
volumes:
|
|
||||||
- /host/path/to/config:/config
|
|
||||||
ports:
|
|
||||||
- "8181:8080"
|
|
||||||
depends_on:
|
|
||||||
db:
|
|
||||||
condition: service_healthy
|
|
||||||
labels:
|
|
||||||
- "traefik.http.routers.gowatch.rule=Host(`192.168.178.254`)"
|
|
||||||
- "traefik.http.routers.gowatch.middlewares=test-auth"
|
|
||||||
db:
|
|
||||||
image: postgres:15
|
|
||||||
environment:
|
|
||||||
- POSTGRES_USER=gorm
|
|
||||||
- POSTGRES_PASSWORD=gorm
|
|
||||||
- POSTGRES_DB=gorm
|
|
||||||
volumes:
|
|
||||||
- /host/path/to/db:/var/lib/postgresql/data
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
depends_on:
|
|
||||||
- proxy
|
|
||||||
proxy:
|
|
||||||
image: traefik:v2.9.6
|
|
||||||
command: --providers.docker
|
|
||||||
labels:
|
|
||||||
- "traefik.http.middlewares.test-auth.basicauth.users=broodjeaap:$$2y$$10$$aUvoh7HNdt5tvf8PYMKaaOyCLD3Uel03JtEIPxFEBklJE62VX4rD6"
|
|
||||||
ports:
|
|
||||||
- "8080:80"
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
|
||||||
```
|
|
||||||
|
|
||||||
Change the `Host` label to the correct ip/hostname and generate a user/password string with [htpasswd](https://httpd.apache.org/docs/2.4/programs/htpasswd.html) for the `basicauth.users` label, note that the `$` character is escaped with `$$`
|
|
||||||
|
|
||||||
# Filters
|
|
||||||
|
|
||||||
GoWatch comes with many filters that should, hopefully, be enough to allow for most use cases.
|
|
||||||
|
|
||||||
## Cron
|
|
||||||
|
|
||||||
The `cron` filter is used to schedule when your watch will run.
|
|
||||||
It uses the [cron](https://pkg.go.dev/github.com/robfig/cron/v3@v3.0.0#section-readme) package to schedule go routines, some common examples would be:
|
|
||||||
- `@every 15m`: will trigger every 15 minutes starting on server start.
|
|
||||||
- `@hourly`: will trigger every beginning of hour.
|
|
||||||
- `30 * * * *`: will trigger every hour on the half hour.
|
|
||||||
|
|
||||||
For more detailed instructions you can check its documentation.
|
|
||||||
|
|
||||||
## Get URL
|
|
||||||
|
|
||||||
Fetches the given URL and outputs the HTTP response.
|
|
||||||
For more complicated requests, POSTing/headers/login, use the [HTTP functionality](https://github.com/vadv/gopher-lua-libs/tree/master/http#client-1) in the Lua filter.
|
|
||||||
During editing, http requests are cached, so not to trigger any DOS protection on your sources.
|
|
||||||
|
|
||||||
## Get URLs
|
|
||||||
|
|
||||||
Fetches every URL given as input and outputs every HTTP response.
|
|
||||||
During editing, http requests are cached, so not to trigger any DOS protection on your sources.
|
|
||||||
|
|
||||||
## CSS
|
|
||||||
|
|
||||||
Use a [CSS selector](https://www.w3schools.com/cssref/css_selectors.php) to filter your http responses.
|
|
||||||
The [Cascadia](https://github.com/andybalholm/cascadia) package is used for this filter, check the docs to see what is and isn't supported.
|
|
||||||
|
|
||||||
## XPath
|
|
||||||
|
|
||||||
Use an [XPath](https://www.w3schools.com/xml/xpath_intro.asp) to filter your http responses.
|
|
||||||
The [XPath](https://github.com/antchfx/xpath) package is used for this filter, check the docs to see what is and isn't supported.
|
|
||||||
|
|
||||||
## JSON
|
|
||||||
|
|
||||||
Use a this to filter your JSON responses, the [gjson](https://github.com/tidwall/gjson) package is used for this filter.
|
|
||||||
Some common examples would be:
|
|
||||||
- product.price
|
|
||||||
- items.3
|
|
||||||
- products.#.price
|
|
||||||
|
|
||||||
## Replace
|
|
||||||
|
|
||||||
Simple replace filter, supports regular expressions.
|
|
||||||
If the `With` value is empty, it will just remove matching text.
|
|
||||||
|
|
||||||
## Match
|
|
||||||
|
|
||||||
Searches for the regex, outputs every match.
|
|
||||||
|
|
||||||
## Substring
|
|
||||||
|
|
||||||
Substring allows for a [Python like](https://learnpython.com/blog/substring-of-string-python/) substring selection.
|
|
||||||
For the input string 'Hello World!':
|
|
||||||
- `:5`: Hello
|
|
||||||
- `6:`: World!
|
|
||||||
- `6,0,7`: WHo
|
|
||||||
- `-6:`: World!
|
|
||||||
- `-6:,:5`: World!Hello
|
|
||||||
|
|
||||||
## Contains
|
|
||||||
|
|
||||||
Inputs pass if they contain the given value, basically same as Match but more basic.
|
|
||||||
|
|
||||||
## Store
|
|
||||||
|
|
||||||
Stores each input value in the database under its own name, should probably limit this to single inputs (after Minimum/Maximum/Average filters).
|
|
||||||
|
|
||||||
## Notify
|
|
||||||
|
|
||||||
Executes the given template and sends the resulting string as a message to the given notifier(s).
|
|
||||||
It uses the [Golang templating language](https://pkg.go.dev/text/template), the outputs of all the filters can be used by the name of the filters.
|
|
||||||
So if you have a `Min` filter like in the example, it can be referenced in the template by using `{{ .Min }}`.
|
|
||||||
The name of the watch is also included under `.WatchName`.
|
|
||||||
|
|
||||||
To configure notifiers see the [notifiers](#notifiers) section.
|
|
||||||
|
|
||||||
## Math
|
|
||||||
|
|
||||||
### Sum
|
|
||||||
|
|
||||||
Sums the inputs together, nonnumerical values are skipped.
|
|
||||||
### Minimum
|
|
||||||
Outputs the lowest value of the inputs, nonnumerical values are skipped.
|
|
||||||
### Maximum
|
|
||||||
Outputs the highest value of the inputs, nonnumerical values are skipped.
|
|
||||||
|
|
||||||
### Average
|
|
||||||
Outputs the average of the inputs, nonnumerical values are skipped.
|
|
||||||
|
|
||||||
### Count
|
|
||||||
Outputs the number of inputs.
|
|
||||||
### Round
|
|
||||||
Outputs the inputs rounded to the given decimals, nonnumerical valuesa are skipped.
|
|
||||||
## Condition
|
|
||||||
|
|
||||||
### Different Than Last
|
|
||||||
|
|
||||||
Passes an input if it is different than the last stored value.
|
|
||||||
|
|
||||||
### Lower Than Last
|
|
||||||
|
|
||||||
Passes an input if it is lower than the last stored value.
|
|
||||||
|
|
||||||
### Lowest
|
|
||||||
|
|
||||||
Passes an input if it is lower than all previous stored values.
|
|
||||||
|
|
||||||
### Lower Than
|
|
||||||
|
|
||||||
Passes an input if it is lower than a given value.
|
|
||||||
|
|
||||||
### Higher Than Last
|
|
||||||
|
|
||||||
Passes an input if it is higher than the last stored value.
|
|
||||||
|
|
||||||
### Highest
|
|
||||||
Passes an input if it is higher than all previous stored values.
|
|
||||||
|
|
||||||
### Higher Than
|
|
||||||
|
|
||||||
Passes an input if it is higher than a given value.
|
|
||||||
|
|
||||||
## Lua
|
|
||||||
|
|
||||||
The Lua filter wraps [gopher-lua](https://github.com/yuin/gopher-lua), with [gopher-lua-libs](https://github.com/vadv/gopher-lua-libs) to greatly extend the capabilities of the Lua VM.
|
|
||||||
A basic script that just passes all inputs to the output looks like this:
|
|
||||||
```
|
|
||||||
for i,input in pairs(inputs) do
|
|
||||||
table.insert(outputs, input)
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Both `inputs` and `outputs` are convenience tables provided by GoWatch to make Lua scripting a bit easier.
|
|
||||||
There is also a `logs` table that can be used the same way as the `outputs` table (`table.insert(logs, 'this will be logged')`) to provide some basic logging.
|
|
||||||
|
|
||||||
Much of the functionality that is provided through individual filters in GoWatch can also be done from Lua.
|
|
||||||
The gopher-lua-libs provide an [http](https://github.com/vadv/gopher-lua-libs/tree/master/http) lib, whose output can be parsed with the [xmlpath](https://github.com/vadv/gopher-lua-libs/tree/master/xmlpath) or [json](https://github.com/vadv/gopher-lua-libs/tree/master/json) libs and then filtered with a [regular expression](https://github.com/vadv/gopher-lua-libs/tree/master/regexp) or some regular Lua scripting to then finally be turned into a ready to send notification through a [template](https://github.com/vadv/gopher-lua-libs/tree/master/template).
|
|
||||||
|
|
||||||
# Notifiers
|
|
||||||
|
|
||||||
The basic form is:
|
|
||||||
```
|
|
||||||
notifiers:
|
|
||||||
<notifier-name>:
|
|
||||||
type: "<notifier-type>"
|
|
||||||
other: ""
|
|
||||||
values: ""
|
|
||||||
etc: ""
|
|
||||||
```
|
|
||||||
See below for possible types.
|
|
||||||
## Shoutrrr
|
|
||||||
|
|
||||||
[Shoutrrr](https://containrrr.dev/shoutrrr/v0.5/) can be used to notify many different services, check their docs for a [list](https://containrrr.dev/shoutrrr/v0.5/services/overview/) of which ones.
|
|
||||||
An example config for sending notifications through Shoutrrr:
|
|
||||||
```
|
|
||||||
notifiers:
|
|
||||||
Shoutrrr-telegram-discord:
|
|
||||||
type: "shoutrrr"
|
|
||||||
urls:
|
|
||||||
- telegram://<token>@telegram?chats=<channel-1-id>,<chat-2-id>
|
|
||||||
- discord://<token>@<webhookid>
|
|
||||||
- etc...
|
|
||||||
database:
|
|
||||||
dsn: "watch.db"
|
|
||||||
prune: "@every 1h"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Apprise
|
|
||||||
|
|
||||||
[Apprise](https://github.com/caronc/apprise) can also be used to send notifications, it supports many different services/protocols, but it requires access to an [Apprise API](https://github.com/caronc/apprise-api).
|
|
||||||
Luckily there is a [docker image](https://hub.docker.com/r/caronc/apprise) available that we can add to our compose:
|
|
||||||
```
|
|
||||||
version: "3"
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
image: ghcr.io/broodjeaap/go-watch:latest
|
|
||||||
container_name: go-watch
|
|
||||||
volumes:
|
|
||||||
- /host/path/to/:/config
|
|
||||||
ports:
|
|
||||||
- "8080:8080"
|
|
||||||
apprise:
|
|
||||||
image: caronc/apprise:latest
|
|
||||||
```
|
|
||||||
|
|
||||||
And the notifier config:
|
|
||||||
```
|
|
||||||
notifiers:
|
|
||||||
apprise:
|
|
||||||
type: "apprise"
|
|
||||||
url: "http://apprise:8000/notify"
|
|
||||||
urls:
|
|
||||||
- "tgram://<bot_token>/<chat_id>/"
|
|
||||||
- "discord://<WebhookID>/<WebhookToken>/"
|
|
||||||
database:
|
|
||||||
dsn: "watch.db"
|
|
||||||
prune: "@every 1h"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Telegram
|
|
||||||
|
|
||||||
Get a bot token from the [@BotFather](https://core.telegram.org/bots/tutorial) and get the [chatID](https://www.alphr.com/find-chat-id-telegram/) of the chat want to send the notifications to.
|
|
||||||
An example config for sending notifications through Telegram:
|
|
||||||
```
|
|
||||||
notifiers:
|
|
||||||
Telegram:
|
|
||||||
token: "<token>"
|
|
||||||
chat: "<chatID>"
|
|
||||||
debug: false
|
|
||||||
database:
|
|
||||||
dsn: "watch.db"
|
|
||||||
prune: "@every 1h"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Discord
|
|
||||||
|
|
||||||
To get a token, userID and/or serverID you first have to enable [Developer Mode and create a new application](https://www.ionos.com/digitalguide/server/know-how/creating-discord-bot/).
|
|
||||||
Then you can right click on your username in any chat to copy your user ID or right click on a server/channel to get the server/channel ID.
|
|
||||||
An example config for sending DM notifications through Discord:
|
|
||||||
```
|
|
||||||
notifiers:
|
|
||||||
Discord:
|
|
||||||
type: "discord"
|
|
||||||
token: "<token>"
|
|
||||||
userID: "<userID>"
|
|
||||||
debug: false
|
|
||||||
database:
|
|
||||||
dsn: "watch.db"
|
|
||||||
prune: "@every 1h"
|
|
||||||
```
|
|
||||||
|
|
||||||
An example config for sending channel notifications:
|
|
||||||
```
|
|
||||||
notifiers:
|
|
||||||
Discord:
|
|
||||||
type: "discord"
|
|
||||||
token: "<token>"
|
|
||||||
server:
|
|
||||||
ID: "<serverID>"
|
|
||||||
channel: "<channelID>"
|
|
||||||
debug: false
|
|
||||||
database:
|
|
||||||
dsn: "watch.db"
|
|
||||||
prune: "@every 1h"
|
|
||||||
```
|
|
||||||
|
|
||||||
Both a userID and serverID/channelID is also possible.
|
|
||||||
|
|
||||||
## Email
|
|
||||||
|
|
||||||
An example config for sending email notifications through a SMTP relay server:
|
|
||||||
```
|
|
||||||
notifiers:
|
|
||||||
Email-to-at-email-com:
|
|
||||||
type: "email"
|
|
||||||
server: "smtp.relay.com"
|
|
||||||
port: "465"
|
|
||||||
from: "from@email.com"
|
|
||||||
user: "apikey"
|
|
||||||
password: "-"
|
|
||||||
to: "to@email.com"
|
|
||||||
database:
|
|
||||||
dsn: "watch.db"
|
|
||||||
prune: "@every 1h"
|
|
||||||
```
|
|
||||||
|
|
||||||
# Dev
|
|
||||||
## type script compilation
|
|
||||||
|
|
||||||
`tsc static/*.ts --lib es2020,dom --watch --downlevelIteration`
|
|
||||||
|
|
||||||
# Dependencies
|
|
||||||
|
|
||||||
The following libaries are used in Go-Watch:
|
|
||||||
- [Gin](https://github.com/gin-gonic/gin) for HTTP server
|
|
||||||
- [multitemplate](https://github.com/gin-contrib/multitemplate) for template inheritance
|
|
||||||
- [Cascadia](https://pkg.go.dev/github.com/andybalholm/cascadia) for CSS selectors
|
|
||||||
- [htmlquery](https://pkg.go.dev/github.com/antchfx/htmlquery) for XPath selectors
|
|
||||||
- [validator](https://pkg.go.dev/github.com/go-playground/validator/v10@v10.11.0) for user user input validation
|
|
||||||
- Notifiers
|
|
||||||
- [Shoutrrr](https://github.com/containrrr/shoutrrr/) for many different services
|
|
||||||
- [tgbotapi](https://pkg.go.dev/github.com/go-telegram-bot-api/telegram-bot-api/v5@v5.5.1) for Telegram
|
|
||||||
- [discordgo](https://pkg.go.dev/github.com/bwmarrin/discordgo) for Discord
|
|
||||||
- [gomail](https://pkg.go.dev/gopkg.in/gomail.v2?utm_source=godoc) for Email
|
|
||||||
- [cron](https://pkg.go.dev/github.com/robfig/cron/v3@v3.0.0) for job scheduling
|
|
||||||
- [viper](https://pkg.go.dev/github.com/spf13/viper@v1.12.0) for config management
|
|
||||||
- [gjson](https://pkg.go.dev/github.com/tidwall/gjson@v1.14.2) for JSON selectors
|
|
||||||
- [gopher-lua](https://github.com/yuin/gopher-lua) for Lua scripting
|
|
||||||
- [gopher-lua-libs](https://pkg.go.dev/github.com/vadv/gopher-lua-libs@v0.4.0) for expanding the Lua scripting functionality
|
|
||||||
- [net](https://pkg.go.dev/golang.org/x/net) for http fetching
|
|
||||||
- [gorm](https://pkg.go.dev/gorm.io/gorm@v1.23.8) for database abstraction
|
|
||||||
- [sqlite](https://pkg.go.dev/gorm.io/driver/sqlite@v1.3.6)
|
|
||||||
- [postgres](https://github.com/go-gorm/postgres)
|
|
||||||
- [mysql](https://github.com/go-gorm/mysql)
|
|
||||||
- [sqlserver](https://github.com/go-gorm/sqlserver)
|
|
||||||
- [bootstrap](https://getbootstrap.com/)
|
|
Loading…
Add table
Reference in a new issue