Compare commits

...

41 Commits

Author SHA1 Message Date
stryan b9d50931ad update drone
continuous-integration/drone/push Build is passing Details
2023-11-27 20:36:11 -05:00
stryan b0ad50f907 api update, minor cleanup
continuous-integration/drone/push Build is passing Details
2023-11-27 20:34:32 -05:00
stryan c564430dde update libs
continuous-integration/drone/push Build is passing Details
2023-10-12 22:51:32 -04:00
stryan 1140534496 better room management 2023-06-01 17:45:43 -04:00
stryan 0da253025e Merge pull request 'rewrite-2' (#2) from rewrite-2 into master
continuous-integration/drone/push Build is passing Details
Reviewed-on: #2
2023-05-31 21:04:07 -04:00
stryan e0d5cf6dd8 major rewrite- ,use mbl
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2023-05-30 20:12:38 -04:00
stryan d7b10a5aa5 unecesary 2023-05-30 17:49:38 -04:00
stryan ccabfd7810 back to alpine, don't need cgo anyway
continuous-integration/drone/push Build is passing Details
2023-05-16 18:47:45 -04:00
stryan d30093a39e update containerfile
continuous-integration/drone/push Build is passing Details
2023-05-16 18:40:44 -04:00
stryan aa3c366f71 revert
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-16 18:25:25 -04:00
stryan fc274e665a handle rooms dynamically
continuous-integration/drone/push Build is passing Details
2023-05-15 19:21:02 -04:00
stryan 0d5a3ff5b9 republic things since json, set dimension server
continuous-integration/drone/push Build is passing Details
2023-05-14 21:05:52 -04:00
stryan ae50a68aea gofumpt
continuous-integration/drone/push Build is passing Details
2023-05-14 19:13:23 -04:00
stryan ba285c4e49 more cleanup
continuous-integration/drone/push Build is passing Details
2023-05-14 19:09:49 -04:00
stryan 0a34a59328 initial cleanup 2023-05-14 18:45:55 -04:00
stryan de2e8d014a check error
continuous-integration/drone/push Build is passing Details
2023-05-14 17:39:08 -04:00
stryan 4938f28ace add api key
continuous-integration/drone/push Build is passing Details
2023-04-24 18:01:40 -04:00
stryan 2c10ea6f33 don't ping with noone there 2023-01-24 21:25:42 -05:00
stryan c875585c4b more text, make podman 2023-01-17 23:53:18 -05:00
stryan a8c0c96505 add subs 2023-01-17 22:49:57 -05:00
stryan 599540b342 actually save filter
continuous-integration/drone/push Build is passing Details
2022-08-10 14:03:35 -04:00
stryan 0eef4a7547 docker service file
continuous-integration/drone/push Build is passing Details
2022-08-07 15:28:11 -04:00
stryan 057c9f1d99 add containerfile, use accountdatastore
continuous-integration/drone/push Build is passing Details
2022-08-07 14:57:09 -04:00
stryan a8ea567352 update readme
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2021-12-08 13:30:08 -05:00
stryan 4f12bdc1c9 use access token when available
continuous-integration/drone/push Build is passing Details
continuous-integration/drone Build is passing Details
2021-12-08 13:22:56 -05:00
stryan e8a0b29c0b typo 2021-12-04 11:09:50 -05:00
stryan 4ca54eb7db move count logic to embed section, cuz we know it's right there
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2021-12-01 13:57:21 -05:00
stryan 2eff2a3559 use tag info in version
continuous-integration/drone/push Build is passing Details
2021-10-27 12:06:54 -04:00
stryan 6f22f580dd actually count things properly
continuous-integration/drone/tag Build is passing Details
2021-10-26 14:33:22 -04:00
stryan 67fe7dea38 bit of clean up
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2021-10-26 14:14:26 -04:00
stryan 9b64ca67cd add stat tracking 2021-10-26 14:01:58 -04:00
stryan 5697c5ecd8 update drone config
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2021-10-15 12:36:16 -04:00
stryan 149c1753b8 update drone config
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is failing Details
2021-10-15 12:34:18 -04:00
stryan ada88655fd update deps
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2021-10-14 17:19:20 -04:00
stryan f033a2d798 update mod name 2021-10-13 17:28:47 -04:00
stryan 5fd4a5e52b typos 2021-09-16 16:46:15 -04:00
stryan df73a5fa9e don't log self messages
continuous-integration/drone/push Build is passing Details
2021-08-23 19:55:54 -04:00
stryan 47c9142763 expose config setting in info
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2021-08-23 19:49:20 -04:00
stryan 82f0d7af58 add options for not annoucing, show title 2021-08-23 19:44:20 -04:00
Gitea 4bddee9ee9 playbook adjustments
continuous-integration/drone/push Build is passing Details
2021-08-07 14:08:17 -04:00
stryan 72b8192f3b add normal config spot
continuous-integration/drone/tag Build is passing Details
continuous-integration/drone/push Build is passing Details
2021-08-07 13:36:26 -04:00
16 changed files with 746 additions and 742 deletions

7
.containerignore Normal file
View File

@ -0,0 +1,7 @@
.git
.gitignore
Dockerfile*
README.md
LICENSE
*.yaml
statefile

View File

@ -1,28 +1,19 @@
kind: pipeline kind: pipeline
name: default name: default
steps: steps:
- name: build - name: submodules
image: golang image: alpine/git
commands: commands:
- make - git submodule update --init --recursive
- name: build-container
- name: create release tar image: plugins/docker
image: golang settings:
commands: repo: git.saintnet.tech/stryan/simpbot
- mkdir dist registry: git.saintnet.tech
- tar -czf dist/release-$DRONE_TAG-$DRONE_STAGE_ARCH.tar.gz init/simpbot.service config.yaml.sample simpbot password:
when: from_secret: build_pass
event: tag username:
from_secret: build_username
- name: release dockerfile: Containerfile
image: plugins/gitea-release tags: latest
settings: layers: true
api_key:
from_secret: gitea_token
files:
- dist/*
base_url: https://git.saintnet.tech
when:
event: tag

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
config.yaml config.yaml
statedata statedata
config.yml config.yml
simpbot

13
Containerfile Normal file
View File

@ -0,0 +1,13 @@
FROM golang:1.20 as builder
WORKDIR /go/src/app
COPY . .
RUN apt update && apt upgrade -y
RUN CGO_ENABLED=0 go build
FROM alpine:latest as final
WORKDIR /srv/
RUN mkdir /srv/simpbot
RUN mkdir -p /lib64
COPY --from=builder /go/src/app/simpbot /srv/simpbot/
CMD ["/srv/simpbot/simpbot"]

View File

@ -2,16 +2,20 @@ GOFILES=$(wildcard *.go)
PROG=simpbot PROG=simpbot
#set variables #set variables
GIT_COMMIT := $(shell git rev-list -1 HEAD) GIT_COMMIT := $(shell git rev-list -1 HEAD)
GIT_TAG := $(shell git describe --tags | cut -d- -f1)
ifeq ($(PREFIX),) # PREFIX is environment variable, but if it is not set, then set default value ifeq ($(PREFIX),) # PREFIX is environment variable, but if it is not set, then set default value
PREFIX := /usr/local PREFIX := /usr/local
endif endif
$(PROG): $(GOFILES) $(PROG): $(GOFILES)
go build -ldflags "-X main.GitCommit=$(GIT_COMMIT)" go build -ldflags "-X main.GitCommit=$(GIT_COMMIT) -X main.GitTag=$(GIT_TAG)"
clean: clean:
rm -f simpbot rm -f simpbot
podman: $(GOFILES)
podman build -t git.saintnet.tech/stryan/$(PROG) .
install: $(SIMPBOT) install: $(SIMPBOT)
install -d $(DESTDIR)$(PREFIX)/bin install -d $(DESTDIR)$(PREFIX)/bin
install -m 755 $(PROG) $(DESTDIR)$(PREFIX)/bin install -m 755 $(PROG) $(DESTDIR)$(PREFIX)/bin

View File

@ -6,7 +6,10 @@ Mostly just a toy for widget testing purposes, and to have something to convert
Not the most productive use of my time. Not the most productive use of my time.
## Usage ## Usage
Copy config.yaml.sample to config.yaml, give it a username and password for an account on your Matrix server. Copy config.yaml.sample to config.yaml, give it a username and password or supply an access token for an account on your Matrix server.
The bot will default to using an access_token after logging in succesfully once.
Define some VTuber's to ~~simp for~~ keep track of. The "msg" parameter defines what message is sent when a stream starts for that Vtuber. Define some VTuber's to ~~simp for~~ keep track of. The "msg" parameter defines what message is sent when a stream starts for that Vtuber.
Run the bot. It will automatically add and remove streams as they start and finish, Run the bot. It will automatically add and remove streams as they start and finish,

View File

@ -1,46 +0,0 @@
---
- hosts: localhost
tasks:
- name: check simpbot version
uri:
url: https://git.saintnet.tech/api/v1/repos/stryan/simpbot/releases?limit=1
return_content: true
register: simpbot_latest
- name: "installing simpbot {{ simpbot_latest.json.tag_name }}"
loop: "{{ simpbot_latest.json.[0].assets }}"
when: "'amd64.tar.gz' in item.name"
unarchive:
remote_src: yes
src: "{{ item.browser_download_url }}"
dest: "/tmp/simpbot"
keep_newer: yes
extra_opts:
- --strip=1
- --no-anchored
- simpbot
- name: installing simpbot binary
copy:
src: /tmp/simpbot/simpbot
dest: /usr/local/bin/
register: new_binary
- name: installing unit file
copy:
src: /tmp/simpbot/init/simpbot.service
dest: /etc/systemd/system/simpbot.service
register: new_unit
- name: reload systemd with new unit
systemd:
daemon_reload: yes
when: new_unit || new_binary
- name: start service
systemd:
name: simpbot
state: restarted
when: new_binary

40
go.mod
View File

@ -1,11 +1,37 @@
module simpbot module git.saintnet.tech/stryan/simpbot
go 1.15 go 1.20
require ( require (
github.com/fsnotify/fsnotify v1.4.9 // indirect git.saintnet.tech/stryan/matrixbotlib v0.1.5
github.com/spf13/viper v1.7.1 github.com/charmbracelet/log v0.2.5
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 // indirect gopkg.in/yaml.v2 v2.4.0
maunium.net/go/mautrix v0.9.12 maunium.net/go/mautrix v0.16.1
)
require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/lipgloss v0.9.1 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/kelseyhightower/envconfig v1.4.0 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rs/zerolog v1.31.0 // indirect
github.com/tidwall/gjson v1.17.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
go.mau.fi/util v0.1.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sys v0.13.0 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
maunium.net/go/maulogger/v2 v2.4.1 // indirect
) )

419
go.sum
View File

@ -1,348 +1,83 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= git.saintnet.tech/stryan/matrixbotlib v0.1.5 h1:/p/1KKSveR57lFWGhU617i9nllW0Hh9dhnTeeIfQgMg=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= git.saintnet.tech/stryan/matrixbotlib v0.1.5/go.mod h1:scipuBfwVWNzIp46qj+bSLQ+lzZ47luzcOcTSdF7Jo0=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= github.com/charmbracelet/log v0.2.5 h1:1yVvyKCKVV639RR4LIq1iy1Cs1AKxuNO+Hx2LJtk7Wc=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= github.com/charmbracelet/log v0.2.5/go.mod h1:nQGK8tvc4pS9cvVEH/pWJiZ50eUq1aoXUOjGpXvdD0k=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts=
github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 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/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= go.mau.fi/util v0.1.0 h1:BwIFWIOEeO7lsiI2eWKFkWTfc5yQmoe+0FYyOFVyaoE=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= go.mau.fi/util v0.1.0/go.mod h1:AxuJUMCxpzgJ5eV9JbPWKRH8aAJJidxetNdUj7qcb84=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tidwall/gjson v1.6.8/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI=
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/sjson v1.1.5/go.mod h1:VuJzsZnTowhSxWdOgsAnb886i4AjEyTkk7tNtsL7EYE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d h1:1aflnvSoWWLI2k/dMUAl5lvU1YO4Mb4hz0gh+1rjcxU=
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 h1:ADo5wSpq2gqaCGQWzk7S5vd//0iyyLeAratkEoG5dLE=
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= maunium.net/go/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= maunium.net/go/mautrix v0.16.1 h1:Wb3CvOCe8A/NLsFeZYxKrgXKiqeZUQEBD1zqm7n/kWk=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= maunium.net/go/mautrix v0.16.1/go.mod h1:2Jf15tulVtr6LxoiRL4smRXwpkGWUNfBFhwh/aXDBuk=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
maunium.net/go/maulogger/v2 v2.2.4/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A=
maunium.net/go/mautrix v0.9.12 h1:iNsKOCyK/BqN6Q/7A1aLj1B0/lMbr81LIg+Lry1Vjuo=
maunium.net/go/mautrix v0.9.12/go.mod h1:7IzKfWvpQtN+W2Lzxc0rLvIxFM3ryKX6Ys3S/ZoWbg8=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=

View File

@ -0,0 +1,11 @@
[Unit]
Description=Simpbot: automated appreciation of strange internet women
Requires=docker.service
After=docker.service
[Service]
Restart=always
ExecStart=/usr/bin/docker run --name simpbot --mount=type=bind,source=/etc/simpbot/config.yaml,destination=/etc/simpbot/config.yaml git.saintnet.tech/stryan/simpbot
ExecStop=/usr/bin/docker rm -f simpbot

View File

@ -1,85 +0,0 @@
package main
import (
"bytes"
"encoding/gob"
"os"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/id"
)
type LazyMemStore struct {
mem *mautrix.InMemoryStore
NextBatch map[id.UserID]string
saveFile string
}
func NewLazyMemStore(fileloc string) *LazyMemStore {
return &LazyMemStore{
mem: mautrix.NewInMemoryStore(),
NextBatch: make(map[id.UserID]string),
saveFile: fileloc,
}
}
func (l *LazyMemStore) SaveFilterID(userID id.UserID, filterID string) {
l.mem.SaveFilterID(userID, filterID)
}
func (l *LazyMemStore) LoadFilterID(userID id.UserID) string {
return l.mem.LoadFilterID(userID)
}
func (l *LazyMemStore) SaveNextBatch(userID id.UserID, nextBatchToken string) {
b := new(bytes.Buffer)
l.NextBatch[userID] = nextBatchToken
e := gob.NewEncoder(b)
err := e.Encode(l.NextBatch)
if err != nil {
panic(err)
}
if err := os.WriteFile(l.saveFile, b.Bytes(), 0666); err != nil {
panic(err)
}
}
func (l *LazyMemStore) LoadNextBatch(userID id.UserID) string {
dat, err := os.ReadFile(l.saveFile)
if err != nil {
if os.IsNotExist(err) {
b := new(bytes.Buffer)
e := gob.NewEncoder(b)
err := e.Encode(l.NextBatch)
if err != nil {
panic(err)
}
if err := os.WriteFile(l.saveFile, b.Bytes(), 0666); err != nil {
panic(err)
}
dat, err = os.ReadFile(l.saveFile)
if err != nil {
panic(err)
}
} else {
panic(err)
}
}
d := gob.NewDecoder(bytes.NewBuffer(dat))
err = d.Decode(&l.NextBatch)
if err != nil {
panic(err)
}
return l.NextBatch[userID]
}
func (l *LazyMemStore) SaveRoom(room *mautrix.Room) {
l.mem.SaveRoom(room)
}
func (l *LazyMemStore) LoadRoom(roomID id.RoomID) *mautrix.Room {
return l.mem.LoadRoom(roomID)
}

View File

@ -3,7 +3,7 @@ package main
import "time" import "time"
type StreamList struct { type StreamList struct {
Total string `json:"total"` Total int `json:"total"`
Streams []Stream `json:"items"` Streams []Stream `json:"items"`
} }
type Channel struct { type Channel struct {

231
main.go
View File

@ -1,169 +1,98 @@
package main package main
import ( import (
"fmt" "net/url"
"log"
"os" "os"
"os/signal" "os/signal"
"strings" "sync"
"syscall"
"time"
"github.com/fsnotify/fsnotify" mbl "git.saintnet.tech/stryan/matrixbotlib"
"github.com/spf13/viper"
"maunium.net/go/mautrix" "github.com/charmbracelet/log"
"maunium.net/go/mautrix/event"
) )
var Homeserver string //GitTag is current git tag
var Username string var GitTag string
var Password string
var DimensionServer string //GitCommit is current git commit
var HomeserverDomain string
var GitCommit string var GitCommit string
var Statefile string
func main() { func main() {
viper.SetConfigName("config") log.Info("loading matrix client")
viper.AddConfigPath(".") cnffile := "config.yaml"
err := viper.ReadInConfig() conf, err := mbl.LoadMatrixClientConfig(cnffile)
if err != nil { if err != nil {
log.Fatalf("Fatal error config file: %v \n", err) log.Info("no local config found, checking /etc/simpbot")
cnffile = "/etc/simpbot/config.yaml"
conf, err = mbl.LoadMatrixClientConfig(cnffile)
if err != nil {
panic(err)
}
} }
viper.SetConfigType("yaml")
Homeserver = viper.GetString("homeserver")
viper.SetDefault("domain", Homeserver)
viper.SetDefault("statefile", "simpstate")
Username = viper.GetString("username")
Password = viper.GetString("password")
DimensionServer = viper.GetString("dimension")
HomeserverDomain = viper.GetString("domain")
Statefile = viper.GetString("statefile")
var vtubers []*Vtuber
log.Println("Logging into", Homeserver, "as", Username)
client, err := mautrix.NewClient(Homeserver, "", "")
if err != nil {
panic(err)
}
client.Store = NewLazyMemStore(Statefile)
_, err = client.Login(&mautrix.ReqLogin{
Type: "m.login.password",
Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: Username},
Password: Password,
StoreCredentials: true,
})
if err != nil {
panic(err)
}
fmt.Println("Login successful")
syncer := client.Syncer.(*mautrix.DefaultSyncer)
syncer.OnEventType(event.EventMessage, func(source mautrix.EventSource, evt *event.Event) {
fmt.Printf("<%[1]s> %[4]s (%[2]s/%[3]s)\n", evt.Sender, evt.Type.String(), evt.ID, evt.Content.AsMessage().Body)
body := evt.Content.AsMessage().Body
body_s := strings.Split(body, " ")
if body_s[0] != "!simp" {
return
}
if len(body_s) < 2 {
return //nothing to parse
}
if evt.Sender == client.UserID {
return //ignore events from self
}
switch body_s[1] {
case "info":
// print info page
var infomsg string
vlist := []string{}
for _, vt := range vtubers {
vlist = append(vlist, vt.Name)
}
infomsg = fmt.Sprintf("Currently Simping For: \n%v", strings.Join(vlist, "\n"))
client.SendText(evt.RoomID, infomsg)
case "version":
// print version
client.SendText(evt.RoomID, "SimpBot version "+GitCommit)
case "reload":
//reload config
client.SendText(evt.RoomID, "unimplemented")
case "help":
client.SendText(evt.RoomID, "Supported commands: info,version")
default:
//command not found
client.SendText(evt.RoomID, "command not recognized")
}
})
syncer.OnEventType(event.StateMember, func(source mautrix.EventSource, evt *event.Event) {
fmt.Printf("<%[1]s> %[4]s (%[2]s/%[3]s)\n", evt.Sender, evt.Type.String(), evt.ID, evt.Content.AsMessage().Body)
if evt.Content.AsMember().Membership.IsInviteOrJoin() {
_, err = client.JoinRoomByID(evt.RoomID)
if err != nil {
fmt.Printf("error joining room %v", evt.RoomID)
} else {
fmt.Println("joined room")
}
}
})
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGUSR1)
vtubers = LoadVtubers()
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Config file changed,reloading vtubers:", e.Name)
vtubers = LoadVtubers()
})
matrixClient, err := mbl.NewMatrixClient(conf, nil)
if err != nil {
panic(err)
}
log.Info("loading simpbot")
simp, err := newSimp(cnffile, matrixClient)
if err != nil {
log.Fatal(err)
}
err = simp.SetupMatrix()
if err != nil {
log.Fatal(err)
}
var wg sync.WaitGroup
stop := make(chan bool)
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() { go func() {
for { <-c
time.Sleep(30 * time.Second) log.Info("trying to shutdown cleanly")
roomResp, err := client.JoinedRooms() simp.Stop()
if err != nil { stop <- true
log.Println("error gettting joined rooms")
log.Println(err)
log.Println("Skipping iteration")
continue
}
rooms := roomResp.JoinedRooms
// We're going to assume they're only stream one video at a time
for _, v := range vtubers {
v.Update()
if v.IsLive() {
for _, room := range rooms {
//check to see if already embeded
var content YoutubeWidget
err = client.StateEvent(room, event.NewEventType("im.vector.modular.widgets"), "dimension-m.video-simp-"+v.Name, &content)
if content.ID == "" {
client.SendText(room, v.LiveMsg)
resp, err := client.SendStateEvent(room, event.NewEventType("im.vector.modular.widgets"), "dimension-m.video-simp-"+v.Name, NewYT(v.Name+"'s stream", v.CurrentStream, string(room)))
if err != nil {
log.Println("error embeding video")
log.Println(err)
}
log.Printf("Embed event %v", resp)
}
}
} else {
//Not live, check to see if there's any embeds and remove them
for _, room := range rooms {
var content YoutubeWidget
err = client.StateEvent(room, event.NewEventType("im.vector.modular.widgets"), "dimension-m.video-simp-"+v.Name, &content)
if err == nil && content.ID != "" {
//event found, kill it
resp, err := client.SendStateEvent(room, event.NewEventType("im.vector.modular.widgets"), "dimension-m.video-simp-"+v.Name, struct{}{})
if err != nil {
log.Println("error removed video embed")
log.Println(err)
}
log.Printf("Emebed remove %v", resp)
}
}
}
}
}
}() }()
err = client.Sync() wg.Add(1)
if err != nil { go func() {
panic(err) log.Debug("starting matrix client")
} err = matrixClient.Sync()
if err != nil {
log.Error(err)
}
wg.Done()
log.Info("matrix shutdown")
}()
wg.Add(1)
go func() {
log.Debug("starting simpbot sync")
err = simp.Sync()
if err != nil {
log.Error(err)
}
wg.Done()
log.Info("simpbot shutdown")
}()
log.Info("simpbot running")
wg.Wait()
log.Info("shutting down")
}
func isValidURL(toTest string) bool {
_, err := url.ParseRequestURI(toTest)
if err != nil {
return false
}
u, err := url.Parse(toTest)
if err != nil || u.Scheme == "" || u.Host == "" {
return false
}
return true
} }

441
simp.go Normal file
View File

@ -0,0 +1,441 @@
package main
import (
"errors"
"fmt"
"net/url"
"os"
"strings"
"time"
mbl "git.saintnet.tech/stryan/matrixbotlib"
"github.com/charmbracelet/log"
"golang.org/x/exp/slices"
"gopkg.in/yaml.v2"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
// SimpConf is the simpbot conf that's not matrix
type SimpConf struct {
DimensionServer string `yaml:"dimension"`
HolodexToken string `yaml:"api_token"`
Vtubers []vtuberConfig `yaml:"vtubers"`
}
type simpState struct {
client *mautrix.Client
startTime time.Time
currStream int
maxStream int
vtubers []*Vtuber
stop chan bool
stopRun chan bool
change chan int
rooms []id.RoomID
filename string
}
type simp struct {
Config *SimpConf
State *simpState
}
func newSimp(filename string, client *mautrix.Client) (*simp, error) {
cnf := &SimpConf{}
state := &simpState{
startTime: time.Now(),
stop: make(chan bool),
stopRun: make(chan bool),
change: make(chan int),
client: client,
}
yamlFile, err := os.ReadFile(filename)
if err == nil {
err = yaml.Unmarshal(yamlFile, cnf)
} else {
return nil, err
}
if err != nil {
return nil, err
}
state.filename = filename
for _, vt := range cnf.Vtubers {
log.Infof("adding vtuber %v", vt)
v := newVtuber(vt.Name, vt.ChannelID, vt.LiveMsg, vt.Announce)
state.vtubers = append(state.vtubers, v)
}
log.Print(cnf)
if len(state.vtubers) == 0 {
return nil, fmt.Errorf("no vtubers defined")
}
if cnf.HolodexToken == "" {
return nil, fmt.Errorf("no holodex token")
}
if cnf.DimensionServer == "" {
return nil, fmt.Errorf("no dimension server")
}
return &simp{cnf, state}, nil
}
func (s *simp) Sync() error {
go s.Run()
for _, v := range s.State.vtubers {
err := v.Update(s.Config.HolodexToken)
if err != nil {
log.Error("error pinging holodex", "error", err)
continue
}
}
for _, r := range s.State.rooms {
for _, v := range s.State.vtubers {
var content YoutubeWidget
err := s.State.client.StateEvent(
r,
event.NewEventType("im.vector.modular.widgets"),
"dimension-m.video-simp-"+v.Name,
&content,
)
if err != nil {
if !errors.Is(err, mautrix.MNotFound) {
log.Errorf("error getting state event in room %v: %v", r, err)
}
}
embed := content.ID != ""
if v.IsLive() != embed {
err := s.Update(v)
if err != nil {
log.Warn("error updating", "error", err)
}
}
}
}
log.Info("starting normal sync")
ticker := time.NewTicker(time.Second * 45)
for {
select {
case <-s.State.stop:
s.State.stopRun <- true
return nil
case <-ticker.C:
for i, v := range s.State.vtubers {
cur := v.IsLive()
err := v.Update(s.Config.HolodexToken)
if err != nil {
log.Error("error pinging holodex", "error", err)
continue
}
now := v.IsLive()
if cur != now {
s.State.change <- i
}
}
}
}
}
func (s *simp) PopulateRoom(room id.RoomID) error {
log.Info("updating room", "room", room)
for _, v := range s.State.vtubers {
log.Printf("Updating vtuber %v", v.Name)
if v.IsLive() {
if v.AnnounceLive {
_, err := s.State.client.SendText(room, v.LiveMsg)
if err != nil {
log.Warn("error sending message to room", "error", err)
}
} else {
if isValidURL(v.LiveMsg) {
_, err := s.State.client.SendNotice(room, fmt.Sprintf("%v has gone live", v.Name))
if err != nil {
log.Warn("error updating state", "error", err)
}
} else {
_, err := s.State.client.SendNotice(room, v.LiveMsg)
if err != nil {
log.Warn("error updating state", "error", err)
}
}
}
_, err := s.State.client.SendNotice(room, fmt.Sprintf("%v's Title: %v", v.Name, v.CurrentStreamTitle))
if err != nil {
log.Warn("error sending notice", "error", err)
}
var subs string
for k := range v.Subs {
subs += k.String() + " "
}
if len(v.Subs) > 0 {
_, err = s.State.client.SendText(room, fmt.Sprintf("Pinging %v", subs))
if err != nil {
log.Warn("error sending text", "error", err)
}
}
resp, err := s.State.client.SendStateEvent(
room,
event.NewEventType("im.vector.modular.widgets"),
"dimension-m.video-simp-"+v.Name,
s.NewYT(v.Name+"'s stream", v.CurrentStream, string(room)),
)
if err != nil {
return err
}
v.TotalStreams = v.TotalStreams + 1
s.State.currStream++
if s.State.currStream > s.State.maxStream {
s.State.maxStream = s.State.currStream
}
log.Info("Embed stream added", "event", resp, "vtuber", v.Name)
}
}
return nil
}
func (s *simp) Update(v *Vtuber) error {
for _, room := range s.State.rooms {
if v.IsLive() {
if v.AnnounceLive {
_, err := s.State.client.SendText(room, v.LiveMsg)
if err != nil {
log.Warn("error sending text", "error", err)
}
} else {
if isValidURL(v.LiveMsg) {
_, err := s.State.client.SendNotice(room, fmt.Sprintf("%v has gone live", v.Name))
if err != nil {
return err
}
} else {
_, err := s.State.client.SendNotice(room, v.LiveMsg)
if err != nil {
return err
}
}
}
_, err := s.State.client.SendNotice(room, fmt.Sprintf("%v's Title: %v", v.Name, v.CurrentStreamTitle))
if err != nil {
log.Warn("error sending notice", "error", err)
}
var subs string
for k := range v.Subs {
subs += k.String() + " "
}
if len(v.Subs) > 0 {
_, err = s.State.client.SendText(room, fmt.Sprintf("Pinging %v", subs))
if err != nil {
log.Warn("error sending text", "error", err)
}
}
resp, err := s.State.client.SendStateEvent(
room,
event.NewEventType("im.vector.modular.widgets"),
"dimension-m.video-simp-"+v.Name,
s.NewYT(v.Name+"'s stream", v.CurrentStream, string(room)),
)
if err != nil {
return err
}
v.TotalStreams = v.TotalStreams + 1
s.State.currStream++
if s.State.currStream > s.State.maxStream {
s.State.maxStream = s.State.currStream
}
log.Info("Embed stream added", "event", resp, "vtuber", v.Name)
} else {
resp, err := s.State.client.SendStateEvent(room, event.NewEventType("im.vector.modular.widgets"), "dimension-m.video-simp-"+v.Name, struct{}{})
if err != nil {
return err
}
s.State.currStream--
log.Info("Embed stream removed", "event", resp, "vtuber", v.Name)
}
}
return nil
}
func (s *simp) Run() {
for {
select {
case <-s.State.stopRun:
log.Info("stopping runner")
return
case v := <-s.State.change:
err := s.Update(s.State.vtubers[v])
if err != nil {
log.Warn("error updating vtube memory", "error", err)
}
}
}
}
func (s *simp) SetupMatrix() error {
err := mbl.SetupAccountDataStore(s.State.client, "s.batch")
if err != nil {
return err
}
syncer := s.State.client.Syncer.(*mautrix.DefaultSyncer)
uid := s.State.client.UserID.String()
syncer.OnEventType(event.EventMessage, func(_ mautrix.EventSource, evt *event.Event) {
if evt.Sender == s.State.client.UserID {
return // ignore events from self
}
log.Debugf("<%[1]s> %[4]s (%[2]s/%[3]s)\n", evt.Sender, evt.Type.String(), evt.ID, evt.Content.AsMessage().Body)
var cmd []string
cmd, err = mbl.ParseCommand(evt, "simp")
if err != nil {
if err != mbl.ErrCmdParseNoPrefix {
log.Printf("invalid command: %v", err)
}
return
}
switch cmd[0] {
case "info":
// print info page
var infomsg string
vlist := []string{}
for _, vt := range s.State.vtubers {
ann := ""
if vt.AnnounceLive {
ann = "*"
}
vlist = append(vlist, fmt.Sprintf("%v%v", vt.Name, ann))
}
infomsg = fmt.Sprintf("Currently Simping For: \n%v", strings.Join(vlist, "\n"))
_, err = s.State.client.SendText(evt.RoomID, infomsg)
if err != nil {
log.Warn("error sending text", "error", err)
}
case "stats":
var statmsg string
vlist := []string{}
t := 0
for _, vt := range s.State.vtubers {
vlist = append(vlist, fmt.Sprintf("%v Total:%v", vt.Name, vt.TotalStreams))
t = t + vt.TotalStreams
}
statmsg = fmt.Sprintf(
"Current Stats Since %v:\n%v\n\nTotal Streams: %v\nMost Concurrent: %v/%v\n",
s.State.startTime,
strings.Join(vlist, "\n"),
t,
s.State.maxStream,
len(s.State.vtubers),
)
_, err = s.State.client.SendText(evt.RoomID, statmsg)
if err != nil {
log.Warn("error sending text", "error", err)
}
case "version":
_, err = s.State.client.SendText(evt.RoomID, "not implemented")
if err != nil {
log.Warn("error sending text", "error", err)
}
case "subscribe":
if len(cmd) < 3 {
_, err = s.State.client.SendText(evt.RoomID, "Need a member to subscribe to")
if err != nil {
log.Warn("error sending text", "error", err)
}
}
vt := cmd[1]
var subbed bool
for _, v := range s.State.vtubers {
if strings.EqualFold(v.Name, vt) {
v.Subs[evt.Sender] = true
subbed = true
}
}
if subbed {
_, err = s.State.client.SendText(evt.RoomID, "subbed")
if err != nil {
log.Warn("error sending text", "error", err)
}
} else {
_, err = s.State.client.SendText(evt.RoomID, "could not identify talent to subscribe to")
if err != nil {
log.Warn("error sending text", "error", err)
}
}
case "help":
_, err = s.State.client.SendText(evt.RoomID, "Supported commands: info,version,stats,subscribe")
if err != nil {
log.Warn("error sending text", "error", err)
}
default:
// command not found
_, err = s.State.client.SendText(evt.RoomID, "command not recognized")
if err != nil {
log.Warn("error sending text", "error", err)
}
}
})
syncer.OnEventType(event.StateMember, func(_ mautrix.EventSource, evt *event.Event) {
// log.Infof("<%[1]s> %[4]s (%[2]s/%[3]s)", evt.Sender, evt.Type.String(), evt.ID, evt.Content.AsMessage().Body)
mevt := evt.Content.AsMember()
log.Printf("FBL %v", mevt.Membership)
if mevt.Membership == event.MembershipInvite {
_, err = s.State.client.JoinRoomByID(evt.RoomID)
if err != nil {
log.Errorf("error joining room %v", evt.RoomID)
} else {
log.Infof("joined room %v", evt.RoomID)
s.State.rooms = append(s.State.rooms, evt.RoomID)
}
} else if evt.Content.AsMember().Membership.IsLeaveOrBan() {
if evt.StateKey != nil && *evt.StateKey == uid {
log.Infof("leaving room %v", evt.RoomID)
index := slices.Index(s.State.rooms, evt.RoomID)
if index != -1 {
slices.Delete(s.State.rooms, index, index)
} else {
log.Warn("asked to leave room not in memory: %v", evt.RoomID)
}
}
}
})
roomResp, err := s.State.client.JoinedRooms()
if err != nil {
return err
}
s.State.rooms = roomResp.JoinedRooms
log.Infof("initial room count: %v", len(s.State.rooms))
return nil
}
func (s *simp) Stop() {
s.State.stop <- true
s.State.client.StopSync()
}
func (s *simp) NewYT(videoName, videoID, roomID string) *YoutubeWidget {
encodedVod := url.QueryEscape("https://youtube.com/embed/" + videoID)
return &YoutubeWidget{
Type: "im.vector.modular.widgets",
URL: "https://" + s.Config.DimensionServer + "/widgets/video?url=" + encodedVod,
Name: videoName,
Data: VideoData{
VideoURL: "https://www.youtube.com/watch?v=" + videoID,
URL: "https://youtube.com/embed/" + videoID,
DimensionAppMetadata: DimensionAppMetadata{
InRoomID: roomID,
WrapperURLBase: "https://" + s.Config.DimensionServer + "/widgets/video?url=",
WrapperID: "video",
ScalarWrapperID: "youtube",
Integration: Integration{
Category: "widget",
Type: "youtube",
},
LastUpdatedTS: time.Now().UnixNano() / int64(time.Millisecond),
},
},
CreatorUserID: string(s.State.client.UserID),
ID: "dimension-m.video-simp",
}
}

View File

@ -3,80 +3,88 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"log"
"net/http" "net/http"
"github.com/spf13/viper" "maunium.net/go/mautrix/id"
) )
type VtuberConfig struct { type vtuberConfig struct {
Name string `mapstructure:"name"` Name string `yaml:"name"`
ChannelID string `mapstructure:"channelid"` ChannelID string `yaml:"channelid"`
LiveMsg string `mapstructure:"msg"` LiveMsg string `yaml:"msg"`
Announce bool `yaml:"announce"`
} }
// Vtuber represents a vtuber
type Vtuber struct { type Vtuber struct {
Name string Name string
ChannelID string ChannelID string
CurrentStream string CurrentStream string
LiveMsg string CurrentStreamTitle string
LiveMsg string
AnnounceLive bool
TotalStreams int
Subs map[id.UserID]bool
} }
func NewVtuber(name, channelID, liveMsg string) *Vtuber { func newVtuber(name, channelID, liveMsg string, announce bool) *Vtuber {
return &Vtuber{ return &Vtuber{
Name: name, Name: name,
ChannelID: channelID, ChannelID: channelID,
CurrentStream: "", CurrentStream: "",
LiveMsg: liveMsg, CurrentStreamTitle: "",
LiveMsg: liveMsg,
AnnounceLive: announce,
TotalStreams: 0,
Subs: make(map[id.UserID]bool),
} }
} }
func LoadVtubers() []*Vtuber { // IsLive returns whether the specified Vtuber is live
var vtubersRaw []VtuberConfig
var vtubers []*Vtuber
err := viper.UnmarshalKey("vtubers", &vtubersRaw)
if err != nil {
panic(err)
}
for _, vt := range vtubersRaw {
log.Printf("adding vtuber %v", vt)
vtubers = append(vtubers, NewVtuber(vt.Name, vt.ChannelID, vt.LiveMsg))
}
return vtubers
}
func (v *Vtuber) IsLive() bool { func (v *Vtuber) IsLive() bool {
return v.CurrentStream != "" return v.CurrentStream != ""
} }
func (v *Vtuber) Update() error { // Update takes an apiKey and updates the vtuber struct
url := fmt.Sprintf("https://holodex.net/api/v2/live?channel_id=%s&lang=all&sort=available_at&order=desc&limit=25&offset=0&paginated=%%3Cempty%%3E", v.ChannelID) func (v *Vtuber) Update(apiKey string) error {
url := fmt.Sprintf(
"https://holodex.net/api/v2/live?channel_id=%s&lang=all&sort=available_at&order=desc&limit=25&offset=0&paginated=%%3Cempty%%3E",
v.ChannelID,
)
req, err := http.NewRequest("GET", url, nil) req, err := http.NewRequest("GET", url, nil)
req.Header.Set("X-APIKEY", apiKey)
if err != nil { if err != nil {
return err return fmt.Errorf("error creating GET %w", err)
} }
res, err := http.DefaultClient.Do(req) res, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
return err return fmt.Errorf("error sending request %w", err)
}
defer func() { _ = res.Body.Close() }()
if res.StatusCode != 200 {
return fmt.Errorf("bad status code %v", res.StatusCode)
}
jsonBody, err := io.ReadAll(res.Body)
if err != nil {
return fmt.Errorf("error reading response: %w", err)
} }
defer res.Body.Close()
jsonBody, _ := ioutil.ReadAll(res.Body)
var sl StreamList var sl StreamList
err = json.Unmarshal(jsonBody, &sl) err = json.Unmarshal(jsonBody, &sl)
if err != nil { if err != nil {
log.Printf("error parsing json for vtuber %v:%v", v.Name, err) return fmt.Errorf("error marshalling vtuber: %w %v", err, string(jsonBody))
return err
} }
found := false found := false
for _, s := range sl.Streams { for _, s := range sl.Streams {
if s.Status == "live" { if s.Status == "live" {
v.CurrentStream = s.ID v.CurrentStream = s.ID
v.CurrentStreamTitle = s.Title
found = true found = true
} }
} }
if !found { if !found {
v.CurrentStream = "" v.CurrentStream = ""
v.CurrentStreamTitle = ""
} }
return nil return nil
} }

View File

@ -1,10 +1,5 @@
package main package main
import (
"net/url"
"time"
)
type Integration struct { type Integration struct {
Category string `json:"category"` Category string `json:"category"`
Type string `json:"type"` Type string `json:"type"`
@ -15,49 +10,20 @@ type DimensionAppMetadata struct {
WrapperID string `json:"wrapperId"` WrapperID string `json:"wrapperId"`
ScalarWrapperID string `json:"scalarWrapperId"` ScalarWrapperID string `json:"scalarWrapperId"`
Integration Integration `json:"integration"` Integration Integration `json:"integration"`
LastUpdatedTs int64 `json:"lastUpdatedTs"` LastUpdatedTS int64 `json:"lastUpdatedTs"`
} }
type Data struct { type VideoData struct {
VideoURL string `json:"videoUrl"` VideoURL string `json:"videoUrl"`
URL string `json:"url"` URL string `json:"url"`
DimensionAppMetadata DimensionAppMetadata `json:"dimension:app:metadata"` DimensionAppMetadata DimensionAppMetadata `json:"dimension:app:metadata"`
} }
type YoutubeWidget struct { type YoutubeWidget struct {
Type string `json:"type"` Type string `json:"type"`
URL string `json:"url"` URL string `json:"url"`
Name string `json:"name"` Name string `json:"name"`
Data Data `json:"data"` Data VideoData `json:"data"`
CreatorUserID string `json:"creatorUserId"` CreatorUserID string `json:"creatorUserId"`
ID string `json:"id"` ID string `json:"id"`
RoomID string `json:"roomId"` RoomID string `json:"roomId"`
EventID string `json:"eventId"` EventID string `json:"eventId"`
}
type Unsigned struct {
Age int `json:"age"`
}
func NewYT(videoName, videoID, roomID string) *YoutubeWidget {
encodedVod := url.QueryEscape("https://youtube.com/embed/" + videoID)
return &YoutubeWidget{
Type: "im.vector.modular.widgets",
URL: "https://" + DimensionServer + "/widgets/video?url=" + encodedVod,
Name: videoName,
Data: Data{
VideoURL: "https://www.youtube.com/watch?v=" + videoID,
URL: "https://youtube.com/embed/" + videoID,
DimensionAppMetadata: DimensionAppMetadata{
InRoomID: roomID,
WrapperURLBase: "https://" + DimensionServer + "/widgets/video?url=",
WrapperID: "video",
ScalarWrapperID: "youtube",
Integration: Integration{
Category: "widget",
Type: "youtube",
},
LastUpdatedTs: time.Now().UnixNano() / int64(time.Millisecond),
},
},
CreatorUserID: "@" + Username + ":" + HomeserverDomain,
ID: "dimension-m.video-simp",
}
} }