Compare commits
49
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1a19ce2c8 | ||
|
|
8065d4d7d6 | ||
|
|
7b94487ea0 | ||
|
|
6e5aaa0266 | ||
|
|
e5f8d513f1 | ||
|
|
55b395b271 | ||
|
|
927275d5b3 | ||
|
|
5bc5eaefd1 | ||
|
|
65b3bb39e3 | ||
|
|
2da878f3d6 | ||
|
|
55209e711e | ||
|
|
e53694155b | ||
|
|
083640a173 | ||
|
|
17efb1fcea | ||
|
|
72275a1baf | ||
|
|
7209e38cc8 | ||
|
|
33df91ab8f | ||
|
|
69fab0e7a3 | ||
|
|
bb39f1f1b4 | ||
|
|
a3e59c75a3 | ||
|
|
c6c4b1eb91 | ||
|
|
a87f10d985 | ||
|
|
98f09f73a4 | ||
|
|
3f1a55d1fe | ||
|
|
f369f3c54e | ||
|
|
95006e2bad | ||
|
|
54a83838ed | ||
|
|
eaf7ce2dce | ||
|
|
160b46d890 | ||
|
|
4531cccb04 | ||
|
|
6ee4651f37 | ||
|
|
7fab42ab1b | ||
|
|
cfdf047aeb | ||
|
|
4dd76a12c1 | ||
|
|
ae50ff6fcb | ||
|
|
722f27a678 | ||
|
|
32fe245f24 | ||
|
|
6d5698840a | ||
|
|
5f212e3a3c | ||
|
|
6a30e7b1e7 | ||
|
|
2799fa7e28 | ||
|
|
63434f7849 | ||
|
|
de9119662f | ||
|
|
9beee7497d | ||
|
|
077cb36382 | ||
|
|
98fe3216ad | ||
|
|
84caaee9d5 | ||
|
|
465e22163f | ||
|
|
eb4110e1e6 |
@@ -1,5 +0,0 @@
|
|||||||
# Service release contract
|
|
||||||
|
|
||||||
Butler resolves this repository's protected source revision to verified immutable images. The platform updates only image references in the existing production Compose file and restarts only the already running application services. It preserves current environment, storage, routes, and provider configuration. It retains previous application images and restores them if readiness fails.
|
|
||||||
|
|
||||||
Migration and maintenance images are pinned when declared, but this release operation does not run database migrations or maintenance jobs. A release requiring a schema change needs the corresponding explicit platform migration first. No homelab image-pin commit is required for an ordinary compatible application release.
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 1,
|
|
||||||
"deployment": "geeksbot-release",
|
|
||||||
"environment": "production",
|
|
||||||
"images": {
|
|
||||||
"geeksbot_release_geeksbot_image": {
|
|
||||||
"name": "geeksbot",
|
|
||||||
"tag": "sha-{revision}"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"revision_variable": "geeksbot_release_revision"
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
.git
|
|
||||||
.github
|
|
||||||
.gitea
|
|
||||||
.env
|
|
||||||
.idea
|
|
||||||
geeksbot
|
|
||||||
Binary file not shown.
@@ -1,158 +0,0 @@
|
|||||||
name: Build and Push Container
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
tags:
|
|
||||||
- v*
|
|
||||||
workflow_dispatch: null
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number
|
|
||||||
|| github.run_id }}
|
|
||||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-and-push:
|
|
||||||
runs-on: butler-ci
|
|
||||||
env:
|
|
||||||
REGISTRY: gitea.wayfinderak.com
|
|
||||||
IMAGE_NAME: gitea.wayfinderak.com/wayfinderak/geeksbot
|
|
||||||
container:
|
|
||||||
options: --dns 172.16.30.10
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: https://github.com/actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Select relevant delivery work
|
|
||||||
id: delivery
|
|
||||||
env:
|
|
||||||
DELIVERY_EVENT: ${{ github.event_name }}
|
|
||||||
DELIVERY_REF: ${{ github.ref }}
|
|
||||||
DELIVERY_SHA: ${{ github.sha }}
|
|
||||||
DELIVERY_BASE: ${{ github.event.pull_request.base.sha || github.event.before }}
|
|
||||||
run: |
|
|
||||||
cd .
|
|
||||||
bash scripts/ci-select.sh
|
|
||||||
- name: Install Go and Docker CLI if needed
|
|
||||||
if: ${{ steps.delivery.outputs.run == 'true' }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
need_apt=0
|
|
||||||
need_apk=0
|
|
||||||
if ! command -v go >/dev/null 2>&1 || ! command -v docker >/dev/null 2>&1; then
|
|
||||||
if command -v apt-get >/dev/null 2>&1; then
|
|
||||||
need_apt=1
|
|
||||||
elif command -v apk >/dev/null 2>&1; then
|
|
||||||
need_apk=1
|
|
||||||
else
|
|
||||||
echo "No supported package manager found" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$need_apt" -eq 1 ]; then
|
|
||||||
apt-get update
|
|
||||||
if ! command -v go >/dev/null 2>&1; then
|
|
||||||
apt-get install -y golang-go
|
|
||||||
fi
|
|
||||||
if ! command -v docker >/dev/null 2>&1; then
|
|
||||||
apt-get install -y docker.io curl dnsutils iputils-ping
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$need_apk" -eq 1 ]; then
|
|
||||||
if ! command -v go >/dev/null 2>&1; then
|
|
||||||
apk add --no-cache go
|
|
||||||
fi
|
|
||||||
if ! command -v docker >/dev/null 2>&1; then
|
|
||||||
apk add --no-cache docker-cli curl bind-tools iputils
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
go version
|
|
||||||
docker version
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
if: ${{ steps.delivery.outputs.run == 'true' }}
|
|
||||||
run: go test ./...
|
|
||||||
|
|
||||||
- name: Log in to Gitea Container Registry
|
|
||||||
if: ${{ (steps.delivery.outputs.run == 'true') && github.event_name != 'pull_request' }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
for attempt in 1 2 3; do
|
|
||||||
echo "docker login attempt $attempt"
|
|
||||||
if echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login "$REGISTRY" -u "${{ secrets.REGISTRY_USERNAME }}" --password-stdin; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
sleep 5
|
|
||||||
done
|
|
||||||
echo "docker login failed after 3 attempts" >&2
|
|
||||||
exit 1
|
|
||||||
|
|
||||||
- name: Determine image tags
|
|
||||||
if: ${{ steps.delivery.outputs.run == 'true' }}
|
|
||||||
id: meta
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
REF: ${{ gitea.ref }}
|
|
||||||
REF_NAME: ${{ gitea.ref_name }}
|
|
||||||
SHA: ${{ gitea.sha }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
tags=("$IMAGE_NAME:sha-$SHA")
|
|
||||||
|
|
||||||
if [[ "$REF" == refs/tags/* ]]; then
|
|
||||||
version="$REF_NAME"
|
|
||||||
tags+=("$IMAGE_NAME:$version")
|
|
||||||
tags+=("$IMAGE_NAME:latest")
|
|
||||||
else
|
|
||||||
branch="$REF_NAME"
|
|
||||||
branch_safe="$(echo "$branch" | tr '/_' '--')"
|
|
||||||
sha_short="$(echo "$SHA" | cut -c1-7)"
|
|
||||||
tags+=("$IMAGE_NAME:$branch_safe")
|
|
||||||
tags+=("$IMAGE_NAME:$branch_safe-$sha_short")
|
|
||||||
if [[ "$branch" == "main" || "$branch" == "master" ]]; then
|
|
||||||
tags+=("$IMAGE_NAME:latest")
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
printf 'tags<<EOF\n' >> "$GITHUB_OUTPUT"
|
|
||||||
printf '%s\n' "${tags[@]}" >> "$GITHUB_OUTPUT"
|
|
||||||
printf 'EOF\n' >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Build image
|
|
||||||
if: ${{ steps.delivery.outputs.run == 'true' }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
mapfile -t tags <<'EOF'
|
|
||||||
${{ steps.meta.outputs.tags }}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
build_args=()
|
|
||||||
for tag in "${tags[@]}"; do
|
|
||||||
build_args+=(--tag "$tag")
|
|
||||||
done
|
|
||||||
|
|
||||||
docker build --label "org.opencontainers.image.revision=$GITHUB_SHA" "${build_args[@]}" .
|
|
||||||
|
|
||||||
- name: Push image
|
|
||||||
if: ${{ (steps.delivery.outputs.run == 'true') && github.event_name != 'pull_request' }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
while IFS= read -r tag; do
|
|
||||||
[ -n "$tag" ] || continue
|
|
||||||
docker push "$tag"
|
|
||||||
done <<'EOF'
|
|
||||||
${{ steps.meta.outputs.tags }}
|
|
||||||
EOF
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
name: Build and Push Container
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- main
|
|
||||||
tags:
|
|
||||||
- v*
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
REGISTRY: gitea.wayfinderak.com
|
|
||||||
IMAGE_NAME: wayfinderak/geeksbot
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
docker:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Log in to Gitea registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
|
||||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Prepare image tags
|
|
||||||
id: prep
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
short_sha="${GITHUB_SHA::7}"
|
|
||||||
tags="${REGISTRY}/${IMAGE_NAME}:sha-${short_sha}"
|
|
||||||
|
|
||||||
if [[ "${GITHUB_REF_TYPE}" == "branch" && ("${GITHUB_REF_NAME}" == "master" || "${GITHUB_REF_NAME}" == "main") ]]; then
|
|
||||||
tags+=$'\n'"${REGISTRY}/${IMAGE_NAME}:latest"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
|
|
||||||
version="${GITHUB_REF_NAME#v}"
|
|
||||||
tags+=$'\n'"${REGISTRY}/${IMAGE_NAME}:${version}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
{
|
|
||||||
echo 'tags<<EOF'
|
|
||||||
echo "$tags"
|
|
||||||
echo 'EOF'
|
|
||||||
} >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Build and push image
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: true
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
tags: ${{ steps.prep.outputs.tags }}
|
|
||||||
+4
-4
@@ -1,4 +1,4 @@
|
|||||||
.env
|
bot_secrets.json
|
||||||
.idea
|
google_client_secret.json
|
||||||
|
__pycache__/
|
||||||
portainer-app.env
|
logs/*
|
||||||
|
|||||||
Generated
+14
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="R User Library" level="project" />
|
||||||
|
<orderEntry type="library" name="R Skeletons" level="application" />
|
||||||
|
</component>
|
||||||
|
<component name="TestRunnerService">
|
||||||
|
<option name="projectConfiguration" value="py.test" />
|
||||||
|
<option name="PROJECT_TEST_RUNNER" value="py.test" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
Generated
+7
@@ -0,0 +1,7 @@
|
|||||||
|
<component name="ProjectDictionaryState">
|
||||||
|
<dictionary name="Dustin.Pianalto">
|
||||||
|
<words>
|
||||||
|
<w>rcon</w>
|
||||||
|
</words>
|
||||||
|
</dictionary>
|
||||||
|
</component>
|
||||||
Generated
+7
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="JavaScriptSettings">
|
||||||
|
<option name="languageLevel" value="ES6" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/Geeksbot.iml" filepath="$PROJECT_DIR$/.idea/Geeksbot.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
-18
@@ -1,18 +0,0 @@
|
|||||||
FROM golang:1.22-alpine AS builder
|
|
||||||
|
|
||||||
WORKDIR /src
|
|
||||||
RUN apk add --no-cache ca-certificates git tzdata
|
|
||||||
|
|
||||||
COPY go.mod go.sum ./
|
|
||||||
RUN go mod download
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
ARG TARGETARCH=amd64
|
|
||||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=$TARGETARCH go build -o /out/geeksbot ./cmd/geeksbot
|
|
||||||
|
|
||||||
FROM alpine:3.20
|
|
||||||
RUN apk add --no-cache ca-certificates tzdata
|
|
||||||
|
|
||||||
COPY --from=builder /out/geeksbot /usr/local/bin/geeksbot
|
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/local/bin/geeksbot"]
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2020 Dusty.P
|
Copyright (c) 2018 Dusty.P
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
# Portainer deployment notes
|
|
||||||
|
|
||||||
Generated from the current Kubernetes deployment in namespace `discord-bots`. Secret values are in the local `portainer-*.env` files.
|
|
||||||
|
|
||||||
## Kubernetes source
|
|
||||||
|
|
||||||
- Deployment: `geeksbot`
|
|
||||||
- Replicas: `1`
|
|
||||||
- Rolling update: maxSurge `1`, maxUnavailable `1`
|
|
||||||
- minReadySeconds: `120`
|
|
||||||
- Labels/selectors: `app=geeksbot`
|
|
||||||
- Image pull secrets: `none required for Portainer stack`
|
|
||||||
|
|
||||||
## Containers
|
|
||||||
|
|
||||||
- `pgbouncer`: image `timoha/pgbouncer:1.15.0`; env: DATABASE_URL, SERVER_TLS_SSLMODE, AUTH_TYPE; ports: 5432; requests: {'cpu': '500m', 'memory': '256Mi'}; limits: {'cpu': '1', 'memory': '512Mi'}
|
|
||||||
- `geeksbot`: image `gitea.wayfinderak.com/wayfinderak/geeksbot:latest`; env: DATABASE_URL, DISCORD_TOKEN; ports: none; requests: {'cpu': '1', 'memory': '512Mi'}; limits: {'cpu': '2', 'memory': '1Gi'}
|
|
||||||
|
|
||||||
## Portainer files
|
|
||||||
|
|
||||||
- `portainer-stack.yml` - Docker Compose stack to paste/use in Portainer.
|
|
||||||
- `portainer-app.env` - bot runtime secrets/env.
|
|
||||||
|
|
||||||
## Portainer database connection
|
|
||||||
|
|
||||||
This Portainer stack does not include pgbouncer. `DATABASE_URL` in `portainer-app.env` points directly at the external PostgreSQL service copied from the Kubernetes pgbouncer upstream URL.
|
|
||||||
-18
@@ -1,18 +0,0 @@
|
|||||||
package geeksbot
|
|
||||||
|
|
||||||
type Channel struct {
|
|
||||||
ID string
|
|
||||||
Guild Guild
|
|
||||||
Admin bool
|
|
||||||
Default bool
|
|
||||||
NewPatron bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type ChannelService interface {
|
|
||||||
Channel(id string) (Channel, error)
|
|
||||||
CreateChannel(c Channel) (Channel, error)
|
|
||||||
DeleteChannel(c Channel) error
|
|
||||||
GuildChannels(g Guild) ([]Channel, error)
|
|
||||||
UpdateChannel(c Channel) (Channel, error)
|
|
||||||
GetOrCreateChannel(id string, guild_id string) (Channel, error)
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/dustinpianalto/discordgo"
|
|
||||||
"github.com/dustinpianalto/disgoman"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/internal/exts"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/pkg/database"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/pkg/services"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
Token := os.Getenv("DISCORD_TOKEN")
|
|
||||||
dg, err := discordgo.New("Bot " + Token)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("There was an error when creating the Discord Session, ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dg.State.MaxMessageCount = 100
|
|
||||||
dg.StateEnabled = true
|
|
||||||
dg.LogLevel = 3
|
|
||||||
dg.Debug = false
|
|
||||||
|
|
||||||
dg.Identify = discordgo.Identify{
|
|
||||||
Intents: discordgo.MakeIntent(discordgo.IntentsAll),
|
|
||||||
}
|
|
||||||
|
|
||||||
database.ConnectDatabase(os.Getenv("DATABASE_URL"))
|
|
||||||
//database.RunMigrations()
|
|
||||||
services.InitializeServices()
|
|
||||||
|
|
||||||
owners := []string{
|
|
||||||
"351794468870946827",
|
|
||||||
}
|
|
||||||
|
|
||||||
manager := disgoman.CommandManager{
|
|
||||||
Prefixes: getPrefixes,
|
|
||||||
Owners: owners,
|
|
||||||
StatusManager: disgoman.GetDefaultStatusManager(),
|
|
||||||
ErrorChannel: make(chan disgoman.CommandError, 10),
|
|
||||||
Commands: make(map[string]*disgoman.Command),
|
|
||||||
IgnoreBots: true,
|
|
||||||
CheckPermissions: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add Command Handlers
|
|
||||||
exts.AddCommandHandlers(&manager)
|
|
||||||
|
|
||||||
dg.AddHandler(manager.OnMessage)
|
|
||||||
dg.AddHandler(manager.StatusManager.OnReady)
|
|
||||||
|
|
||||||
err = dg.Open()
|
|
||||||
if err != nil {
|
|
||||||
log.Println("There was an error opening the connection, ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the Error handler in a goroutine
|
|
||||||
go ErrorHandler(manager.ErrorChannel)
|
|
||||||
|
|
||||||
log.Println("The Bot is now running.")
|
|
||||||
sc := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
|
|
||||||
<-sc
|
|
||||||
|
|
||||||
log.Println("Shutting Down...")
|
|
||||||
err = dg.Close()
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPrefixes(guildID string) []string {
|
|
||||||
guild, err := services.GuildService.Guild(guildID)
|
|
||||||
if err != nil || len(guild.Prefixes) == 0 {
|
|
||||||
return []string{"G$", "g$"}
|
|
||||||
}
|
|
||||||
return guild.Prefixes
|
|
||||||
}
|
|
||||||
|
|
||||||
func ErrorHandler(ErrorChan chan disgoman.CommandError) {
|
|
||||||
for ce := range ErrorChan {
|
|
||||||
msg := ce.Message
|
|
||||||
if msg == "" {
|
|
||||||
msg = ce.Error.Error()
|
|
||||||
}
|
|
||||||
_, _ = ce.Context.Send(msg)
|
|
||||||
log.Println(ce.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
351794468870946827
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"load_list": [
|
||||||
|
"admin",
|
||||||
|
"events",
|
||||||
|
"rcon",
|
||||||
|
"repl",
|
||||||
|
"patreon",
|
||||||
|
"fun",
|
||||||
|
"utils",
|
||||||
|
"git"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"rcon_enabled" : false,
|
||||||
|
"channel_lockdown" : false,
|
||||||
|
"raid_status" : 0,
|
||||||
|
"pg_filter" : true,
|
||||||
|
"patreon_enabled" : false,
|
||||||
|
"referral_enabled" : false
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
shit
|
||||||
|
piss
|
||||||
|
fuck
|
||||||
|
cunt
|
||||||
|
cock
|
||||||
|
motherfucker
|
||||||
|
tits
|
||||||
|
ballsack
|
||||||
|
bangbros
|
||||||
|
bitch
|
||||||
|
blow job
|
||||||
|
blowjob
|
||||||
|
clit
|
||||||
|
f u c k
|
||||||
|
gangbang
|
||||||
|
gaylord
|
||||||
|
gaysex
|
||||||
|
god damn
|
||||||
|
goddamn
|
||||||
|
homoerotic
|
||||||
|
hotsex
|
||||||
|
jerk-off
|
||||||
|
jerk off
|
||||||
|
masturbat
|
||||||
|
porn
|
||||||
|
pussy
|
||||||
|
pussi
|
||||||
|
skank
|
||||||
|
whore
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
0
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
{"blobsmirk": "https://discordapp.com/api/emojis/414330931059490827.png", "BlobThinking": "https://discordapp.com/api/emojis/408366351602941952.png", "BlobThinkingCool": "https://discordapp.com/api/emojis/414839939117613057.png", "BlobVote": "https://discordapp.com/api/emojis/414840024266309632.png", "BlobDead": "https://discordapp.com/api/emojis/414840075289886722.png", "BlobIdea": "https://discordapp.com/api/emojis/414840112317333544.png", "BlobOK": "https://discordapp.com/api/emojis/414840149059436544.png", "BlobNausea": "https://discordapp.com/api/emojis/414840414672125992.png", "BlobConfused": "https://discordapp.com/api/emojis/414840484377264158.png", "BlobLenny": "https://discordapp.com/api/emojis/414840707010920459.png", "BlobCrying": "https://discordapp.com/api/emojis/414841411117121556.png", "blobsad": "https://discordapp.com/api/emojis/319122469795397632.png", "blobsmile": "https://discordapp.com/api/emojis/319360049887576074.png", "blobthumbsup": "https://discordapp.com/api/emojis/324917738894000130.png", "blobcouncil": "https://discordapp.com/api/emojis/317793691257274378.png", "FeelsBlobMan": "https://discordapp.com/api/emojis/317969827861889026.png", "BlobPoliceAngry": "https://discordapp.com/api/emojis/317969829854314496.png", "OKBlob": "https://discordapp.com/api/emojis/317970202492928002.png", "blobthinking": "https://discordapp.com/api/emojis/318287662068924416.png", "photoblobs": "https://discordapp.com/api/emojis/318013782662053888.png", "bloblul": "https://discordapp.com/api/emojis/356789385875816448.png", "blobthink": "https://discordapp.com/api/emojis/318799923116113921.png", "blobangry": "https://discordapp.com/api/emojis/319359420997828608.png", "blobsob": "https://discordapp.com/api/emojis/393353541122523136.png", "blobsurprised": "https://discordapp.com/api/emojis/319359953263394817.png", "blobwink": "https://discordapp.com/api/emojis/319360115129974786.png", "blobheadache": "https://discordapp.com/api/emojis/319360532291387392.png", "blobcorner": "https://discordapp.com/api/emojis/319360584703541249.png", "blobrofl": "https://discordapp.com/api/emojis/319360614923370496.png", "blobfacepalm": "https://discordapp.com/api/emojis/319360831626412032.png", "blobdead": "https://discordapp.com/api/emojis/319361012908163072.png", "blobshh": "https://discordapp.com/api/emojis/324917371259060224.png", "blobhammer": "https://discordapp.com/api/emojis/324917546098622466.png", "blobhappy": "https://discordapp.com/api/emojis/324917905257136128.png", "blobexpressionless": "https://discordapp.com/api/emojis/324918006331473923.png", "blobokhand": "https://discordapp.com/api/emojis/324918192763961344.png", "blobwhat": "https://discordapp.com/api/emojis/324918397643128832.png", "blobpat": "https://discordapp.com/api/emojis/324918615994531840.png", "blobpopcorn": "https://discordapp.com/api/emojis/324918859540856832.png", "blobblush": "https://discordapp.com/api/emojis/324918940256174089.png", "blobstraightface": "https://discordapp.com/api/emojis/324919073903476766.png", "blobhifive": "https://discordapp.com/api/emojis/324919278845427713.png", "sadblob": "https://discordapp.com/api/emojis/414017193890545685.png", "blob": "https://discordapp.com/api/emojis/401869697588527105.png"}
|
||||||
@@ -0,0 +1,673 @@
|
|||||||
|
A Alpha Time Zone Military
|
||||||
|
UTC +1
|
||||||
|
ACDT Australian Central Daylight Time
|
||||||
|
CDT – Central Daylight Time
|
||||||
|
CDST – Central Daylight Savings Time Australia
|
||||||
|
UTC +10:30
|
||||||
|
ACST Australian Central Standard Time
|
||||||
|
CST – Central Standard Time Australia
|
||||||
|
UTC +9:30
|
||||||
|
ACT Acre Time South America
|
||||||
|
UTC -5
|
||||||
|
ACT Australian Central Time Australia
|
||||||
|
UTC +9:30 / +10:30
|
||||||
|
ACWST Australian Central Western Standard Time Australia
|
||||||
|
UTC +8:45
|
||||||
|
ADT Arabia Daylight Time
|
||||||
|
AST – Arabia Summer Time Asia
|
||||||
|
UTC +3
|
||||||
|
ADT Atlantic Daylight Time
|
||||||
|
ADST – Atlantic Daylight Saving Time
|
||||||
|
AST – Atlantic Summer Time
|
||||||
|
HAA – Heure Avancée de l'Atlantique (French)
|
||||||
|
North America
|
||||||
|
Atlantic
|
||||||
|
UTC -3
|
||||||
|
AEDT Australian Eastern Daylight Time
|
||||||
|
EDT – Eastern Daylight Time
|
||||||
|
EDST – Eastern Daylight Saving Time Australia
|
||||||
|
UTC +11
|
||||||
|
AEST Australian Eastern Standard Time
|
||||||
|
EST – Eastern Standard Time
|
||||||
|
AET – Australian Eastern Time Australia
|
||||||
|
UTC +10
|
||||||
|
AET Australian Eastern Time Australia
|
||||||
|
UTC +10:00 / +11:00
|
||||||
|
AFT Afghanistan Time Asia
|
||||||
|
UTC +4:30
|
||||||
|
AKDT Alaska Daylight Time
|
||||||
|
ADST – Alaska Daylight Saving Time North America
|
||||||
|
UTC -8
|
||||||
|
AKST Alaska Standard Time
|
||||||
|
AT – Alaska Time North America
|
||||||
|
UTC -9
|
||||||
|
ALMT Alma-Ata Time Asia
|
||||||
|
UTC +6
|
||||||
|
AMST Amazon Summer Time South America
|
||||||
|
UTC -3
|
||||||
|
AMST Armenia Summer Time
|
||||||
|
AMDT – Armenia Daylight Time Asia
|
||||||
|
UTC +5
|
||||||
|
AMT Amazon Time South America
|
||||||
|
UTC -4
|
||||||
|
AMT Armenia Time Asia
|
||||||
|
UTC +4
|
||||||
|
ANAST Anadyr Summer Time Asia
|
||||||
|
UTC +12
|
||||||
|
ANAT Anadyr Time Asia
|
||||||
|
UTC +12
|
||||||
|
AQTT Aqtobe Time Asia
|
||||||
|
UTC +5
|
||||||
|
ART Argentina Time Antarctica
|
||||||
|
South America
|
||||||
|
UTC -3
|
||||||
|
AST Arabia Standard Time
|
||||||
|
AST – Arabic Standard Time
|
||||||
|
AST – Al Manamah Standard Time Asia
|
||||||
|
UTC +2
|
||||||
|
AST Atlantic Standard Time
|
||||||
|
AT – Atlantic Time
|
||||||
|
AST – Tiempo Estándar del Atlántico (Spanish)
|
||||||
|
HNA – Heure Normale de l'Atlantique (French)
|
||||||
|
North America
|
||||||
|
Atlantic
|
||||||
|
Caribbean
|
||||||
|
UTC -4
|
||||||
|
AT Atlantic Time North America
|
||||||
|
Atlantic
|
||||||
|
Caribbean
|
||||||
|
UTC -4:00 / -3:00
|
||||||
|
AWDT Australian Western Daylight Time
|
||||||
|
WDT – Western Daylight Time
|
||||||
|
WST – Western Summer Time Australia
|
||||||
|
UTC +9
|
||||||
|
AWST Australian Western Standard Time
|
||||||
|
WST – Western Standard Time
|
||||||
|
WAT – Western Australia Time Australia
|
||||||
|
UTC +8
|
||||||
|
AZOST Azores Summer Time
|
||||||
|
AZODT – Azores Daylight Time Atlantic
|
||||||
|
UTC +0
|
||||||
|
AZOT Azores Time
|
||||||
|
AZOST – Azores Standard Time Atlantic
|
||||||
|
UTC -1
|
||||||
|
AZST Azerbaijan Summer Time Asia
|
||||||
|
UTC +5
|
||||||
|
AZT Azerbaijan Time Asia
|
||||||
|
UTC +4
|
||||||
|
AoE Anywhere on Earth Pacific
|
||||||
|
UTC -12
|
||||||
|
B Bravo Time Zone Military
|
||||||
|
UTC +2
|
||||||
|
BNT Brunei Darussalam Time
|
||||||
|
BDT – Brunei Time Asia
|
||||||
|
UTC +8
|
||||||
|
BOT Bolivia Time South America
|
||||||
|
UTC -4
|
||||||
|
BRST Brasília Summer Time
|
||||||
|
BST – Brazil Summer Time
|
||||||
|
BST – Brazilian Summer Time South America
|
||||||
|
UTC -2
|
||||||
|
BRT Brasília Time
|
||||||
|
BT – Brazil Time
|
||||||
|
BT – Brazilian Time South America
|
||||||
|
UTC -3
|
||||||
|
BST Bangladesh Standard Time Asia
|
||||||
|
UTC +6
|
||||||
|
BST Bougainville Standard Time Pacific
|
||||||
|
UTC +11
|
||||||
|
BST British Summer Time
|
||||||
|
BDT – British Daylight Time
|
||||||
|
BDST – British Daylight Saving Time Europe
|
||||||
|
UTC +1
|
||||||
|
BTT Bhutan Time Asia
|
||||||
|
UTC +6
|
||||||
|
C Charlie Time Zone Military
|
||||||
|
UTC +3
|
||||||
|
CAST Casey Time Antarctica
|
||||||
|
UTC +8
|
||||||
|
CAT Central Africa Time Africa
|
||||||
|
UTC +2
|
||||||
|
CCT Cocos Islands Time Indian Ocean
|
||||||
|
UTC +6:30
|
||||||
|
CDT Central Daylight Time
|
||||||
|
CDST – Central Daylight Saving Time
|
||||||
|
NACDT – North American Central Daylight Time
|
||||||
|
HAC – Heure Avancée du Centre (French)
|
||||||
|
North America
|
||||||
|
UTC -5
|
||||||
|
CDT Cuba Daylight Time Caribbean
|
||||||
|
UTC -4
|
||||||
|
CEST Central European Summer Time
|
||||||
|
CEDT – Central European Daylight Time
|
||||||
|
ECST – European Central Summer Time
|
||||||
|
MESZ – Mitteleuropäische Sommerzeit (German)
|
||||||
|
Europe
|
||||||
|
Antarctica
|
||||||
|
UTC +2
|
||||||
|
CET Central European Time
|
||||||
|
ECT – European Central Time
|
||||||
|
CET – Central Europe Time
|
||||||
|
MEZ – Mitteleuropäische Zeit (German)
|
||||||
|
Europe
|
||||||
|
Africa
|
||||||
|
UTC +1
|
||||||
|
CHADT Chatham Island Daylight Time
|
||||||
|
CDT – Chatham Daylight Time Pacific
|
||||||
|
UTC +13:45
|
||||||
|
CHAST Chatham Island Standard Time Pacific
|
||||||
|
UTC +12:45
|
||||||
|
CHOST Choibalsan Summer Time
|
||||||
|
CHODT – Choibalsan Daylight Time
|
||||||
|
CHODST – Choibalsan Daylight Saving Time Asia
|
||||||
|
UTC +9
|
||||||
|
CHOT Choibalsan Time Asia
|
||||||
|
UTC +8
|
||||||
|
CHUT Chuuk Time Pacific
|
||||||
|
UTC +10
|
||||||
|
CIDST Cayman Islands Daylight Saving Time Caribbean
|
||||||
|
UTC -4
|
||||||
|
CIST Cayman Islands Standard Time
|
||||||
|
CIT – Cayman Islands Time Caribbean
|
||||||
|
UTC -5
|
||||||
|
CKT Cook Island Time Pacific
|
||||||
|
UTC -10
|
||||||
|
CLST Chile Summer Time
|
||||||
|
CLDT – Chile Daylight Time South America
|
||||||
|
Antarctica
|
||||||
|
UTC -3
|
||||||
|
CLT Chile Standard Time
|
||||||
|
CT – Chile Time
|
||||||
|
CLST – Chile Standard Time South America
|
||||||
|
Antarctica
|
||||||
|
UTC -4
|
||||||
|
COT Colombia Time South America
|
||||||
|
UTC -5
|
||||||
|
CST Central Standard Time
|
||||||
|
CT – Central Time
|
||||||
|
NACST – North American Central Standard Time
|
||||||
|
CST – Tiempo Central Estándar (Spanish)
|
||||||
|
HNC – Heure Normale du Centre (French)
|
||||||
|
North America
|
||||||
|
Central America
|
||||||
|
UTC -6
|
||||||
|
CST China Standard Time Asia
|
||||||
|
UTC +8
|
||||||
|
CST Cuba Standard Time Caribbean
|
||||||
|
UTC -5
|
||||||
|
CT Central Time North America
|
||||||
|
Central America
|
||||||
|
UTC -6:00 / -5:00
|
||||||
|
CVT Cape Verde Time Africa
|
||||||
|
UTC -1
|
||||||
|
CXT Christmas Island Time Australia
|
||||||
|
UTC +7
|
||||||
|
ChST Chamorro Standard Time
|
||||||
|
GST – Guam Standard Time Pacific
|
||||||
|
UTC +10
|
||||||
|
D Delta Time Zone Military
|
||||||
|
UTC +4
|
||||||
|
DAVT Davis Time Antarctica
|
||||||
|
UTC +7
|
||||||
|
DDUT Dumont-d'Urville Time Antarctica
|
||||||
|
UTC +10
|
||||||
|
E Echo Time Zone Military
|
||||||
|
UTC +5
|
||||||
|
EASST Easter Island Summer Time
|
||||||
|
EADT – Easter Island Daylight Time Pacific
|
||||||
|
UTC -5
|
||||||
|
EAST Easter Island Standard Time Pacific
|
||||||
|
UTC -6
|
||||||
|
EAT Eastern Africa Time
|
||||||
|
EAT – East Africa Time Africa
|
||||||
|
Indian Ocean
|
||||||
|
UTC +3
|
||||||
|
ECT Ecuador Time South America
|
||||||
|
UTC -5
|
||||||
|
EDT Eastern Daylight Time
|
||||||
|
EDST – Eastern Daylight Savings Time
|
||||||
|
NAEDT – North American Eastern Daylight Time
|
||||||
|
HAE – Heure Avancée de l'Est (French)
|
||||||
|
EDT – Tiempo de verano del Este (Spanish)
|
||||||
|
North America
|
||||||
|
Caribbean
|
||||||
|
UTC -4
|
||||||
|
EEST Eastern European Summer Time
|
||||||
|
EEDT – Eastern European Daylight Time
|
||||||
|
OESZ – Osteuropäische Sommerzeit (German)
|
||||||
|
Europe
|
||||||
|
Asia
|
||||||
|
UTC +3
|
||||||
|
EET Eastern European Time
|
||||||
|
OEZ – Osteuropäische Zeit (German)
|
||||||
|
Europe
|
||||||
|
Asia
|
||||||
|
Africa
|
||||||
|
UTC +2
|
||||||
|
EGST Eastern Greenland Summer Time
|
||||||
|
EGST – East Greenland Summer Time North America
|
||||||
|
UTC +0
|
||||||
|
EGT East Greenland Time
|
||||||
|
EGT – Eastern Greenland Time North America
|
||||||
|
UTC -1
|
||||||
|
EST Eastern Standard Time
|
||||||
|
ET – Eastern Time
|
||||||
|
NAEST – North American Eastern Standard Time
|
||||||
|
ET – Tiempo del Este (Spanish)
|
||||||
|
HNE – Heure Normale de l'Est (French)
|
||||||
|
North America
|
||||||
|
Caribbean
|
||||||
|
Central America
|
||||||
|
UTC -5
|
||||||
|
ET Eastern Time North America
|
||||||
|
Caribbean
|
||||||
|
Central America
|
||||||
|
UTC -5:00 / -4:00
|
||||||
|
F Foxtrot Time Zone Military
|
||||||
|
UTC +6
|
||||||
|
FET Further-Eastern European Time Europe
|
||||||
|
UTC +3
|
||||||
|
FJST Fiji Summer Time
|
||||||
|
FJDT – Fiji Daylight Time Pacific
|
||||||
|
UTC +13
|
||||||
|
FJT Fiji Time Pacific
|
||||||
|
UTC +12
|
||||||
|
FKST Falkland Islands Summer Time
|
||||||
|
FKDT – Falkland Island Daylight Time South America
|
||||||
|
UTC -3
|
||||||
|
FKT Falkland Island Time
|
||||||
|
FKST – Falkland Island Standard Time South America
|
||||||
|
UTC -4
|
||||||
|
FNT Fernando de Noronha Time South America
|
||||||
|
UTC -2
|
||||||
|
G Golf Time Zone Military
|
||||||
|
UTC +7
|
||||||
|
GALT Galapagos Time Pacific
|
||||||
|
UTC -6
|
||||||
|
GAMT Gambier Time
|
||||||
|
GAMT – Gambier Islands Time Pacific
|
||||||
|
UTC -9
|
||||||
|
GET Georgia Standard Time Asia
|
||||||
|
UTC +4
|
||||||
|
GFT French Guiana Time South America
|
||||||
|
UTC -3
|
||||||
|
GILT Gilbert Island Time Pacific
|
||||||
|
UTC +12
|
||||||
|
GMT Greenwich Mean Time
|
||||||
|
UTC – Coordinated Universal Time
|
||||||
|
GT – Greenwich Time Europe
|
||||||
|
Africa
|
||||||
|
North America
|
||||||
|
Antarctica
|
||||||
|
UTC +0
|
||||||
|
GST Gulf Standard Time Asia
|
||||||
|
UTC +4
|
||||||
|
GST South Georgia Time South America
|
||||||
|
UTC -2
|
||||||
|
GYT Guyana Time South America
|
||||||
|
UTC -4
|
||||||
|
H Hotel Time Zone Military
|
||||||
|
UTC +8
|
||||||
|
HADT Hawaii-Aleutian Daylight Time
|
||||||
|
HDT – Hawaii Daylight Time North America
|
||||||
|
UTC -9
|
||||||
|
HAST Hawaii-Aleutian Standard Time
|
||||||
|
HST – Hawaii Standard Time North America
|
||||||
|
Pacific
|
||||||
|
UTC -10
|
||||||
|
HKT Hong Kong Time Asia
|
||||||
|
UTC +8
|
||||||
|
HOVST Hovd Summer Time
|
||||||
|
HOVDT – Hovd Daylight Time
|
||||||
|
HOVDST – Hovd Daylight Saving Time Asia
|
||||||
|
UTC +8
|
||||||
|
HOVT Hovd Time Asia
|
||||||
|
UTC +7
|
||||||
|
I India Time Zone Military
|
||||||
|
UTC +9
|
||||||
|
ICT Indochina Time Asia
|
||||||
|
UTC +7
|
||||||
|
IDT Israel Daylight Time Asia
|
||||||
|
UTC +3
|
||||||
|
IOT Indian Chagos Time Indian Ocean
|
||||||
|
UTC +6
|
||||||
|
IRDT Iran Daylight Time
|
||||||
|
IRST – Iran Summer Time
|
||||||
|
IDT – Iran Daylight Time Asia
|
||||||
|
UTC +4:30
|
||||||
|
IRKST Irkutsk Summer Time Asia
|
||||||
|
UTC +9
|
||||||
|
IRKT Irkutsk Time Asia
|
||||||
|
UTC +8
|
||||||
|
IRST Iran Standard Time
|
||||||
|
IT – Iran Time Asia
|
||||||
|
UTC +3:30
|
||||||
|
IST India Standard Time
|
||||||
|
IT – India Time
|
||||||
|
IST – Indian Standard Time Asia
|
||||||
|
UTC +5:30
|
||||||
|
IST Irish Standard Time
|
||||||
|
IST – Irish Summer Time Europe
|
||||||
|
UTC +1
|
||||||
|
IST Israel Standard Time Asia
|
||||||
|
UTC +2
|
||||||
|
JST Japan Standard Time Asia
|
||||||
|
UTC +9
|
||||||
|
K Kilo Time Zone Military
|
||||||
|
UTC +10
|
||||||
|
KGT Kyrgyzstan Time Asia
|
||||||
|
UTC +6
|
||||||
|
KOST Kosrae Time Pacific
|
||||||
|
UTC +11
|
||||||
|
KRAST Krasnoyarsk Summer Time Asia
|
||||||
|
UTC +8
|
||||||
|
KRAT Krasnoyarsk Time Asia
|
||||||
|
UTC +7
|
||||||
|
KST Korea Standard Time
|
||||||
|
KST – Korean Standard Time
|
||||||
|
KT – Korea Time Asia
|
||||||
|
UTC +9
|
||||||
|
KUYT Kuybyshev Time
|
||||||
|
SAMST – Samara Summer Time Europe
|
||||||
|
UTC +4
|
||||||
|
L Lima Time Zone Military
|
||||||
|
UTC +11
|
||||||
|
LHDT Lord Howe Daylight Time Australia
|
||||||
|
UTC +11
|
||||||
|
LHST Lord Howe Standard Time Australia
|
||||||
|
UTC +10:30
|
||||||
|
LINT Line Islands Time Pacific
|
||||||
|
UTC +14
|
||||||
|
M Mike Time Zone Military
|
||||||
|
UTC +12
|
||||||
|
MAGST Magadan Summer Time
|
||||||
|
MAGST – Magadan Island Summer Time Asia
|
||||||
|
UTC +12
|
||||||
|
MAGT Magadan Time
|
||||||
|
MAGT – Magadan Island Time Asia
|
||||||
|
UTC +11
|
||||||
|
MART Marquesas Time Pacific
|
||||||
|
UTC -9:30
|
||||||
|
MAWT Mawson Time Antarctica
|
||||||
|
UTC +5
|
||||||
|
MDT Mountain Daylight Time
|
||||||
|
MDST – Mountain Daylight Saving Time
|
||||||
|
NAMDT – North American Mountain Daylight Time
|
||||||
|
HAR – Heure Avancée des Rocheuses (French)
|
||||||
|
North America
|
||||||
|
UTC -6
|
||||||
|
MHT Marshall Islands Time Pacific
|
||||||
|
UTC +12
|
||||||
|
MMT Myanmar Time Asia
|
||||||
|
UTC +6:30
|
||||||
|
MSD Moscow Daylight Time
|
||||||
|
Moscow Summer Time Europe
|
||||||
|
UTC +4
|
||||||
|
MSK Moscow Standard Time
|
||||||
|
MCK – Moscow Time Europe
|
||||||
|
Asia
|
||||||
|
UTC +3
|
||||||
|
MST Mountain Standard Time
|
||||||
|
MT – Mountain Time
|
||||||
|
NAMST – North American Mountain Standard Time
|
||||||
|
HNR – Heure Normale des Rocheuses (French)
|
||||||
|
North America
|
||||||
|
UTC -7
|
||||||
|
MT Mountain Time North America
|
||||||
|
UTC -7:00 / -6:00
|
||||||
|
MUT Mauritius Time Africa
|
||||||
|
UTC +4
|
||||||
|
MVT Maldives Time Asia
|
||||||
|
UTC +5
|
||||||
|
MYT Malaysia Time
|
||||||
|
MST – Malaysian Standard Time Asia
|
||||||
|
UTC +8
|
||||||
|
N November Time Zone Military
|
||||||
|
UTC -1
|
||||||
|
NCT New Caledonia Time Pacific
|
||||||
|
UTC +11
|
||||||
|
NDT Newfoundland Daylight Time
|
||||||
|
HAT – Heure Avancée de Terre-Neuve (French)
|
||||||
|
North America
|
||||||
|
UTC -2:30
|
||||||
|
NFT Norfolk Time
|
||||||
|
NFT – Norfolk Island Time Australia
|
||||||
|
UTC +11
|
||||||
|
NOVST Novosibirsk Summer Time
|
||||||
|
OMSST – Omsk Summer Time Asia
|
||||||
|
UTC +7
|
||||||
|
NOVT Novosibirsk Time
|
||||||
|
OMST – Omsk Standard Time Asia
|
||||||
|
UTC +6
|
||||||
|
NPT Nepal Time Asia
|
||||||
|
UTC +5:45
|
||||||
|
NRT Nauru Time Pacific
|
||||||
|
UTC +12
|
||||||
|
NST Newfoundland Standard Time
|
||||||
|
HNT – Heure Normale de Terre-Neuve (French)
|
||||||
|
North America
|
||||||
|
UTC -3:30
|
||||||
|
NUT Niue Time Pacific
|
||||||
|
UTC -11
|
||||||
|
NZDT New Zealand Daylight Time Pacific
|
||||||
|
Antarctica
|
||||||
|
UTC +13
|
||||||
|
NZST New Zealand Standard Time Pacific
|
||||||
|
Antarctica
|
||||||
|
UTC +12
|
||||||
|
O Oscar Time Zone Military
|
||||||
|
UTC -2
|
||||||
|
OMSST Omsk Summer Time
|
||||||
|
NOVST – Novosibirsk Summer Time Asia
|
||||||
|
UTC +7
|
||||||
|
OMST Omsk Standard Time
|
||||||
|
OMST – Omsk Time
|
||||||
|
NOVT – Novosibirsk Time Asia
|
||||||
|
UTC +6
|
||||||
|
ORAT Oral Time Asia
|
||||||
|
UTC +5
|
||||||
|
P Papa Time Zone Military
|
||||||
|
UTC -3
|
||||||
|
PDT Pacific Daylight Time
|
||||||
|
PDST – Pacific Daylight Saving Time
|
||||||
|
NAPDT – North American Pacific Daylight Time
|
||||||
|
HAP – Heure Avancée du Pacifique (French)
|
||||||
|
North America
|
||||||
|
UTC -7
|
||||||
|
PET Peru Time South America
|
||||||
|
UTC -5
|
||||||
|
PETST Kamchatka Summer Time Asia
|
||||||
|
UTC +12
|
||||||
|
PETT Kamchatka Time
|
||||||
|
PETT – Petropavlovsk-Kamchatski Time Asia
|
||||||
|
UTC +12
|
||||||
|
PGT Papua New Guinea Time Pacific
|
||||||
|
UTC +10
|
||||||
|
PHOT Phoenix Island Time Pacific
|
||||||
|
UTC +13
|
||||||
|
PHT Philippine Time
|
||||||
|
PST – Philippine Standard Time Asia
|
||||||
|
UTC +8
|
||||||
|
PKT Pakistan Standard Time
|
||||||
|
PKT – Pakistan Time Asia
|
||||||
|
UTC +5
|
||||||
|
PMDT Pierre & Miquelon Daylight Time North America
|
||||||
|
UTC -2
|
||||||
|
PMST Pierre & Miquelon Standard Time North America
|
||||||
|
UTC -3
|
||||||
|
PONT Pohnpei Standard Time Pacific
|
||||||
|
UTC +11
|
||||||
|
PST Pacific Standard Time
|
||||||
|
PT – Pacific Time
|
||||||
|
NAPST – North American Pacific Standard Time
|
||||||
|
PT – Tiempo del Pacífico (Spanish)
|
||||||
|
HNP – Heure Normale du Pacifique (French)
|
||||||
|
North America
|
||||||
|
UTC -8
|
||||||
|
PST Pitcairn Standard Time Pacific
|
||||||
|
UTC -8
|
||||||
|
PT Pacific Time North America
|
||||||
|
UTC -8:00 / -7:00
|
||||||
|
PWT Palau Time Pacific
|
||||||
|
UTC +9
|
||||||
|
PYST Paraguay Summer Time South America
|
||||||
|
UTC -3
|
||||||
|
PYT Paraguay Time South America
|
||||||
|
UTC -4
|
||||||
|
PYT Pyongyang Time
|
||||||
|
PYST – Pyongyang Standard Time Asia
|
||||||
|
UTC +8:30
|
||||||
|
Q Quebec Time Zone Military
|
||||||
|
UTC -4
|
||||||
|
QYZT Qyzylorda Time Asia
|
||||||
|
UTC +6
|
||||||
|
R Romeo Time Zone Military
|
||||||
|
UTC -5
|
||||||
|
RET Reunion Time Africa
|
||||||
|
UTC +4
|
||||||
|
ROTT Rothera Time Antarctica
|
||||||
|
UTC -3
|
||||||
|
S Sierra Time Zone Military
|
||||||
|
UTC -6
|
||||||
|
SAKT Sakhalin Time Asia
|
||||||
|
UTC +11
|
||||||
|
SAMT Samara Time
|
||||||
|
SAMT – Samara Standard Time Europe
|
||||||
|
UTC +4
|
||||||
|
SAST South Africa Standard Time
|
||||||
|
SAST – South African Standard Time Africa
|
||||||
|
UTC +2
|
||||||
|
SBT Solomon Islands Time
|
||||||
|
SBT – Solomon Island Time Pacific
|
||||||
|
UTC +11
|
||||||
|
SCT Seychelles Time Africa
|
||||||
|
UTC +4
|
||||||
|
SGT Singapore Time
|
||||||
|
SST – Singapore Standard Time Asia
|
||||||
|
UTC +8
|
||||||
|
SRET Srednekolymsk Time Asia
|
||||||
|
UTC +11
|
||||||
|
SRT Suriname Time South America
|
||||||
|
UTC -3
|
||||||
|
SST Samoa Standard Time Pacific
|
||||||
|
UTC -11
|
||||||
|
SYOT Syowa Time Antarctica
|
||||||
|
UTC +3
|
||||||
|
T Tango Time Zone Military
|
||||||
|
UTC -7
|
||||||
|
TAHT Tahiti Time Pacific
|
||||||
|
UTC -10
|
||||||
|
TFT French Southern and Antarctic Time
|
||||||
|
KIT – Kerguelen (Islands) Time Indian Ocean
|
||||||
|
UTC +5
|
||||||
|
TJT Tajikistan Time Asia
|
||||||
|
UTC +5
|
||||||
|
TKT Tokelau Time Pacific
|
||||||
|
UTC +13
|
||||||
|
TLT East Timor Time Asia
|
||||||
|
UTC +9
|
||||||
|
TMT Turkmenistan Time Asia
|
||||||
|
UTC +5
|
||||||
|
TOST Tonga Summer Time Pacific
|
||||||
|
UTC +14
|
||||||
|
TOT Tonga Time Pacific
|
||||||
|
UTC +13
|
||||||
|
TRT Turkey Time Asia
|
||||||
|
Europe
|
||||||
|
UTC +3
|
||||||
|
TVT Tuvalu Time Pacific
|
||||||
|
UTC +12
|
||||||
|
U Uniform Time Zone Military
|
||||||
|
UTC -8
|
||||||
|
ULAST Ulaanbaatar Summer Time
|
||||||
|
ULAST – Ulan Bator Summer Time Asia
|
||||||
|
UTC +9
|
||||||
|
ULAT Ulaanbaatar Time
|
||||||
|
ULAT – Ulan Bator Time Asia
|
||||||
|
UTC +8
|
||||||
|
UTC Coordinated Universal Time Worldwide
|
||||||
|
UTC
|
||||||
|
UYST Uruguay Summer Time South America
|
||||||
|
UTC -2
|
||||||
|
UYT Uruguay Time South America
|
||||||
|
UTC -3
|
||||||
|
UZT Uzbekistan Time Asia
|
||||||
|
UTC +5
|
||||||
|
V Victor Time Zone Military
|
||||||
|
UTC -9
|
||||||
|
VET Venezuelan Standard Time
|
||||||
|
HLV – Hora Legal de Venezuela (Spanish)
|
||||||
|
South America
|
||||||
|
UTC -4
|
||||||
|
VLAST Vladivostok Summer Time Asia
|
||||||
|
UTC +11
|
||||||
|
VLAT Vladivostok Time Asia
|
||||||
|
UTC +10
|
||||||
|
VOST Vostok Time Antarctica
|
||||||
|
UTC +6
|
||||||
|
VUT Vanuatu Time
|
||||||
|
EFATE – Efate Time Pacific
|
||||||
|
UTC +11
|
||||||
|
W Whiskey Time Zone Military
|
||||||
|
UTC -10
|
||||||
|
WAKT Wake Time Pacific
|
||||||
|
UTC +12
|
||||||
|
WARST Western Argentine Summer Time South America
|
||||||
|
UTC -3
|
||||||
|
WAST West Africa Summer Time Africa
|
||||||
|
UTC +2
|
||||||
|
WAT West Africa Time Africa
|
||||||
|
UTC +1
|
||||||
|
WEST Western European Summer Time
|
||||||
|
WEDT – Western European Daylight Time
|
||||||
|
WESZ – Westeuropäische Sommerzeit (German)
|
||||||
|
Europe
|
||||||
|
Africa
|
||||||
|
UTC +1
|
||||||
|
WET Western European Time
|
||||||
|
GMT – Greenwich Mean Time
|
||||||
|
WEZ – Westeuropäische Zeit (German)
|
||||||
|
Europe
|
||||||
|
Africa
|
||||||
|
UTC +0
|
||||||
|
WFT Wallis and Futuna Time Pacific
|
||||||
|
UTC +12
|
||||||
|
WGST Western Greenland Summer Time
|
||||||
|
WGST – West Greenland Summer Time North America
|
||||||
|
UTC -2
|
||||||
|
WGT West Greenland Time
|
||||||
|
WGT – Western Greenland Time North America
|
||||||
|
UTC -3
|
||||||
|
WIB Western Indonesian Time
|
||||||
|
WIB – Waktu Indonesia Barat Asia
|
||||||
|
UTC +7
|
||||||
|
WIT Eastern Indonesian Time
|
||||||
|
WIT – Waktu Indonesia Timur Asia
|
||||||
|
UTC +9
|
||||||
|
WITA Central Indonesian Time
|
||||||
|
WITA – Waktu Indonesia Tengah Asia
|
||||||
|
UTC +8
|
||||||
|
WST West Samoa Time
|
||||||
|
ST – Samoa Time Pacific
|
||||||
|
UTC +14
|
||||||
|
WST Western Sahara Summer Time Africa
|
||||||
|
UTC +1
|
||||||
|
WT Western Sahara Standard Time
|
||||||
|
WT – Western Sahara Time Africa
|
||||||
|
UTC +0
|
||||||
|
X X-ray Time Zone Military
|
||||||
|
UTC -11
|
||||||
|
Y Yankee Time Zone Military
|
||||||
|
UTC -12
|
||||||
|
YAKST Yakutsk Summer Time Asia
|
||||||
|
UTC +10
|
||||||
|
YAKT Yakutsk Time Asia
|
||||||
|
UTC +9
|
||||||
|
YAPT Yap Time Pacific
|
||||||
|
UTC +10
|
||||||
|
YEKST Yekaterinburg Summer Time Asia
|
||||||
|
UTC +6
|
||||||
|
YEKT Yekaterinburg Time Asia
|
||||||
|
UTC +5
|
||||||
|
Z Zulu Time Zone Military
|
||||||
|
UTC +0
|
||||||
Binary file not shown.
@@ -1,72 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: geeksbot
|
|
||||||
namespace: discord-bots
|
|
||||||
labels:
|
|
||||||
app: geeksbot
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: geeksbot
|
|
||||||
strategy:
|
|
||||||
rollingUpdate:
|
|
||||||
maxSurge: 1
|
|
||||||
maxUnavailable: 1
|
|
||||||
minReadySeconds: 120
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: geeksbot
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: pgbouncer
|
|
||||||
image: timoha/pgbouncer:1.15.0
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: "256Mi"
|
|
||||||
cpu: "0.5"
|
|
||||||
limits:
|
|
||||||
memory: "512Mi"
|
|
||||||
cpu: "1"
|
|
||||||
env:
|
|
||||||
- name: DATABASE_URL
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: geeksbot
|
|
||||||
key: pgbouncer_url
|
|
||||||
- name: SERVER_TLS_SSLMODE
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: geeksbot
|
|
||||||
key: pgbouncer_ssl
|
|
||||||
- name: AUTH_TYPE
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: geeksbot
|
|
||||||
key: pgbouncer_auth
|
|
||||||
ports:
|
|
||||||
- containerPort: 5432
|
|
||||||
- name: geeksbot
|
|
||||||
image: <IMAGE>
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: "512Mi"
|
|
||||||
cpu: "1"
|
|
||||||
limits:
|
|
||||||
memory: "1Gi"
|
|
||||||
cpu: "2"
|
|
||||||
env:
|
|
||||||
- name: DATABASE_URL
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: geeksbot
|
|
||||||
key: database_url
|
|
||||||
- name: DISCORD_TOKEN
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: geeksbot
|
|
||||||
key: discord_token
|
|
||||||
imagePullSecrets:
|
|
||||||
- name: registry-1
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
services:
|
|
||||||
geeksbot:
|
|
||||||
image: ${IMAGE:-gitea.wayfinderak.com/wayfinderak/geeksbot:${IMAGE_TAG:-latest}}
|
|
||||||
container_name: geeksbot
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
DATABASE_URL: ${DATABASE_URL}
|
|
||||||
DISCORD_TOKEN: ${DISCORD_TOKEN}
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+295
@@ -0,0 +1,295 @@
|
|||||||
|
import discord
|
||||||
|
from discord.ext import commands
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import inspect
|
||||||
|
import os
|
||||||
|
from .imports import checks
|
||||||
|
|
||||||
|
config_dir = 'config/'
|
||||||
|
admin_id_file = 'admin_ids'
|
||||||
|
extension_dir = 'extensions'
|
||||||
|
owner_id = 351794468870946827
|
||||||
|
embed_color = discord.Colour.from_rgb(49, 107, 111)
|
||||||
|
bot_config_file = 'bot_config.json'
|
||||||
|
invite_match = '(https?://)?(www.)?discord(app.com/(invite|oauth2)|.gg|.io)/[\w\d_\-?=&/]+'
|
||||||
|
|
||||||
|
admin_log = logging.getLogger('admin')
|
||||||
|
|
||||||
|
|
||||||
|
class Admin:
|
||||||
|
def __init__(self, bot):
|
||||||
|
self.bot = bot
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_config_string(guild_config):
|
||||||
|
config_str = ''
|
||||||
|
for config in guild_config:
|
||||||
|
if isinstance(guild_config[config], dict):
|
||||||
|
config_str = f'{config_str}\n{" "*4}{config}'
|
||||||
|
for item in guild_config[config]:
|
||||||
|
config_str = f'{config_str}\n{" "*8}{item}: {guild_config[config][item]}'
|
||||||
|
elif isinstance(guild_config[config], list):
|
||||||
|
config_str = f'{config_str}\n{" "*4}{config}'
|
||||||
|
for item in guild_config[config]:
|
||||||
|
config_str = f'{config_str}\n{" "*8}{item}'
|
||||||
|
else:
|
||||||
|
config_str = f'{config_str}\n{" "*4}{config}: {guild_config[config]}'
|
||||||
|
return config_str
|
||||||
|
|
||||||
|
@commands.command(hidden=True)
|
||||||
|
@commands.is_owner()
|
||||||
|
async def reload_bot_config(self, ctx):
|
||||||
|
with open(f'{config_dir}{bot_config_file}') as file:
|
||||||
|
self.bot.bot_config = json.load(file)
|
||||||
|
del self.bot.bot_config['token']
|
||||||
|
del self.bot.bot_config['db_con']
|
||||||
|
await ctx.send('Config reloaded.')
|
||||||
|
|
||||||
|
@commands.command(hidden=True)
|
||||||
|
@commands.is_owner()
|
||||||
|
async def reboot(self, ctx):
|
||||||
|
await ctx.send('Geeksbot is restarting.')
|
||||||
|
with open(f'{config_dir}reboot', 'w') as f:
|
||||||
|
f.write(f'1\n{ctx.channel.id}')
|
||||||
|
os._exit(1)
|
||||||
|
|
||||||
|
@commands.command(hidden=True)
|
||||||
|
@commands.is_owner()
|
||||||
|
async def get_bot_config(self, ctx):
|
||||||
|
n = 2000
|
||||||
|
config = [str(self.bot.bot_config)[i:i+n] for i in range(0, len(str(self.bot.bot_config)), n)]
|
||||||
|
for conf in config:
|
||||||
|
await ctx.message.author.send(conf)
|
||||||
|
await ctx.send(f'{ctx.message.author.mention} check your DMs.')
|
||||||
|
|
||||||
|
@commands.command(hidden=True)
|
||||||
|
@commands.is_owner()
|
||||||
|
async def update_emojis(self, ctx):
|
||||||
|
emojis = self.bot.emojis
|
||||||
|
for emoji in emojis:
|
||||||
|
if emoji.animated:
|
||||||
|
emoji_code = f'<a:{emoji.name}:{emoji.id}>'
|
||||||
|
else:
|
||||||
|
emoji_code = f'<:{emoji.name}:{emoji.id}>'
|
||||||
|
if self.bot.con.all('select id from geeksbot_emojis where id = %(id)s', {'id': emoji.id}):
|
||||||
|
self.bot.con.run("update geeksbot_emojis set id = %(id)s, name = %(name)s, code = %(emoji_code)s "
|
||||||
|
"where name = %(name)s",
|
||||||
|
{'name': emoji.name, 'id': emoji.id, 'emoji_code': emoji_code})
|
||||||
|
else:
|
||||||
|
self.bot.con.run("insert into geeksbot_emojis(id,name,code) values (%(id)s,%(name)s,%(emoji_code)s)",
|
||||||
|
{'name': emoji.name, 'id': emoji.id, 'emoji_code': emoji_code})
|
||||||
|
await ctx.message.add_reaction('✅')
|
||||||
|
await ctx.send(f'Emojis have been updated in the database.')
|
||||||
|
|
||||||
|
@commands.command(hidden=True)
|
||||||
|
@commands.check(checks.is_guild_owner)
|
||||||
|
async def get_guild_config(self, ctx):
|
||||||
|
config = self.bot.con.one('select * from guild_config where guild_id = %(id)s', {'id': ctx.guild.id})
|
||||||
|
configs = [str(config)[i:i+1990] for i in range(0, len(config), 1990)]
|
||||||
|
await ctx.message.author.send(f'The current config for the {ctx.guild.name} guild is:\n')
|
||||||
|
admin_log.info(configs)
|
||||||
|
for config in configs:
|
||||||
|
await ctx.message.author.send(f'```{config}```')
|
||||||
|
await ctx.send(f'{ctx.message.author.mention} check your DMs.')
|
||||||
|
|
||||||
|
@commands.group(case_insensitive=True)
|
||||||
|
async def set(self, ctx):
|
||||||
|
"""Run help set for more info"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@commands.group(case_insensitive=True)
|
||||||
|
async def add(self, ctx):
|
||||||
|
"""Run help set for more info"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@commands.group(case_insensitive=True)
|
||||||
|
async def remove(self, ctx):
|
||||||
|
"""Run help set for more info"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@set.command(name='admin_chan', aliases=['ac', 'admin_chat', 'admin chat'])
|
||||||
|
async def _admin_channel(self, ctx, channel: discord.TextChannel=None):
|
||||||
|
if ctx.guild:
|
||||||
|
if checks.is_admin(self.bot, ctx):
|
||||||
|
if channel is not None:
|
||||||
|
self.bot.con.run('update guild_config set admin_chat = %(chan)s where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id, 'chan': channel.id})
|
||||||
|
await ctx.send(f'{channel.name} is now set as the Admin Chat channel for this guild.')
|
||||||
|
|
||||||
|
@set.command(name='channel_lockdown', aliases=['lockdown', 'restrict_access', 'cl'])
|
||||||
|
async def _channel_lockdown(self, ctx, config='true'):
|
||||||
|
if ctx.guild:
|
||||||
|
if checks.is_admin(self.bot, ctx):
|
||||||
|
if str(config).lower() == 'true':
|
||||||
|
if self.bot.con.one('select allowed_channels from guild_config where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id}) is []:
|
||||||
|
await ctx.send('Please set at least one allowed channel before running this command.')
|
||||||
|
else:
|
||||||
|
self.bot.con.run('update guild_config set channel_lockdown = True where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id})
|
||||||
|
await ctx.send('Channel Lockdown is now active.')
|
||||||
|
elif str(config).lower() == 'false':
|
||||||
|
if self.bot.con.one('select channel_lockdown from guild_config where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id}):
|
||||||
|
self.bot.con.run('update guild_config set channel_lockdown = False where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id})
|
||||||
|
await ctx.send('Channel Lockdown has been deactivated.')
|
||||||
|
else:
|
||||||
|
await ctx.send('Channel Lockdown is already deactivated.')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'You are not authorized to run this command.')
|
||||||
|
else:
|
||||||
|
await ctx.send('This command must be run from inside a guild.')
|
||||||
|
|
||||||
|
@add.command(name='allowed_channels', aliases=['channel', 'ac'])
|
||||||
|
async def _allowed_channels(self, ctx, *, channels):
|
||||||
|
if ctx.guild:
|
||||||
|
if checks.is_admin(self.bot, ctx):
|
||||||
|
channels = channels.lower().replace(' ', '').split(',')
|
||||||
|
added = ''
|
||||||
|
for channel in channels:
|
||||||
|
chnl = discord.utils.get(ctx.guild.channels, name=channel)
|
||||||
|
if chnl is None:
|
||||||
|
await ctx.send(f'{channel} is not a valid text channel in this guild.')
|
||||||
|
else:
|
||||||
|
admin_log.info('Chan found')
|
||||||
|
if self.bot.con.one('select allowed_channels from guild_config where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id}):
|
||||||
|
if chnl.id in json.loads(self.bot.con.one('select allowed_channels from guild_config '
|
||||||
|
'where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id})):
|
||||||
|
admin_log.info('Chan found in config')
|
||||||
|
await ctx.send(f'{channel} is already in the list of allowed channels. Skipping...')
|
||||||
|
else:
|
||||||
|
admin_log.info('Chan not found in config')
|
||||||
|
allowed_channels = json.loads(self.bot.con.one('select allowed_channels from '
|
||||||
|
'guild_config where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id})).append(chnl.id)
|
||||||
|
self.bot.con.run('update guild_config set allowed_channels = %(channels)s '
|
||||||
|
'where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id, 'channels': allowed_channels})
|
||||||
|
added = f'{added}\n{channel}'
|
||||||
|
else:
|
||||||
|
admin_log.info('Chan not found in config')
|
||||||
|
allowed_channels = [chnl.id]
|
||||||
|
self.bot.con.run('update guild_config set allowed_channels = %(channels)s '
|
||||||
|
'where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id, 'channels': allowed_channels})
|
||||||
|
added = f'{added}\n{channel}'
|
||||||
|
if added != '':
|
||||||
|
await ctx.send(f'The following channels have been added to the allowed channel list: {added}')
|
||||||
|
await ctx.message.add_reaction('✅')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'You are not authorized to run this command.')
|
||||||
|
else:
|
||||||
|
await ctx.send('This command must be run from inside a guild.')
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
@commands.is_owner()
|
||||||
|
async def view_code(self, ctx, code_name):
|
||||||
|
await ctx.send(f"```py\n{inspect.getsource(self.bot.get_command(code_name).callback)}\n```")
|
||||||
|
|
||||||
|
@add.command(aliases=['prefix', 'p'])
|
||||||
|
@commands.cooldown(1, 5, type=commands.BucketType.guild)
|
||||||
|
async def add_prefix(self, ctx, *, prefix=None):
|
||||||
|
if ctx.guild:
|
||||||
|
if checks.is_admin(self.bot, ctx):
|
||||||
|
prefixes = self.bot.con.one('select prefix from guild_config where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id})
|
||||||
|
if prefix is None:
|
||||||
|
await ctx.send(prefixes)
|
||||||
|
return
|
||||||
|
elif prefixes is None:
|
||||||
|
prefixes = prefix.replace(' ', ',').split(',')
|
||||||
|
else:
|
||||||
|
for p in prefix.replace(' ', ',').split(','):
|
||||||
|
prefixes.append(p)
|
||||||
|
if len(prefixes) > 10:
|
||||||
|
await ctx.send(f'Only 10 prefixes are allowed per guild.\nPlease remove some before adding more.')
|
||||||
|
prefixes = prefixes[:10]
|
||||||
|
self.bot.con.run('update guild_config set prefix = %(prefixes)s where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id, 'prefixes': prefixes})
|
||||||
|
await ctx.guild.me.edit(nick=f'[{prefixes[0]}] Geeksbot')
|
||||||
|
await ctx.send(f"Updated. You currently have {len(prefixes)} "
|
||||||
|
f"{'prefix' if len(prefixes) == 1 else 'prefixes'} "
|
||||||
|
f"in your config.\n{', '.join(prefixes)}")
|
||||||
|
else:
|
||||||
|
await ctx.send(f'You are not authorized to run this command.')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'This command must be run from inside a guild.')
|
||||||
|
|
||||||
|
@remove.command(aliases=['prefix', 'p'])
|
||||||
|
@commands.cooldown(1, 5, type=commands.BucketType.guild)
|
||||||
|
async def remove_prefix(self, ctx, *, prefix=None):
|
||||||
|
if ctx.guild:
|
||||||
|
if checks.is_admin(self.bot, ctx):
|
||||||
|
prefixes = self.bot.con.one('select prefix from guild_config where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id})
|
||||||
|
found = 0
|
||||||
|
if prefix is None:
|
||||||
|
await ctx.send(prefixes)
|
||||||
|
return
|
||||||
|
elif prefixes is None or prefixes == []:
|
||||||
|
await ctx.send('There are no custom prefixes setup for this guild.')
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
prefix = prefix.replace(' ', ',').split(',')
|
||||||
|
for p in prefix:
|
||||||
|
if p in prefixes:
|
||||||
|
prefixes.remove(p)
|
||||||
|
found = 1
|
||||||
|
else:
|
||||||
|
await ctx.send(f'The prefix {p} is not in the config for this guild.')
|
||||||
|
if found:
|
||||||
|
self.bot.con.run('update guild_config set prefix = %(prefixes)s where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id, 'prefixes': prefixes})
|
||||||
|
await ctx.guild.me.edit(nick=f'[{prefixes[0] if len(prefixes) != 0 else self.bot.default_prefix}] '
|
||||||
|
f'Geeksbot')
|
||||||
|
await ctx.send(f"Updated. You currently have {len(prefixes)} "
|
||||||
|
f"{'prefix' if len(prefixes) == 1 else 'prefixes'} "
|
||||||
|
f"in your config.\n{', '.join(prefixes)}")
|
||||||
|
else:
|
||||||
|
await ctx.send(f'You are not authorized to run this command.')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'This command must be run from inside a guild.')
|
||||||
|
|
||||||
|
@add.command(name='admin_role', aliases=['admin'])
|
||||||
|
@commands.cooldown(1, 5, type=commands.BucketType.guild)
|
||||||
|
@commands.check(checks.is_guild_owner)
|
||||||
|
async def _add_admin_role(self, ctx, role=None):
|
||||||
|
role = discord.utils.get(ctx.guild.roles, name=role)
|
||||||
|
if role is not None:
|
||||||
|
roles = json.loads(self.bot.con.one('select admin_roles from guild_config where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id}))
|
||||||
|
if role.name in roles:
|
||||||
|
await ctx.send(f'{role.name} is already registered as an admin role in this guild.')
|
||||||
|
else:
|
||||||
|
roles[role.name] = role.id
|
||||||
|
self.bot.con.run('update guild_config set admin_roles = %(roles)s where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id, 'roles': json.dumps(roles)})
|
||||||
|
await ctx.send(f'{role.name} has been added to the list of admin roles for this guild.')
|
||||||
|
else:
|
||||||
|
await ctx.send('You must include a role with this command.')
|
||||||
|
|
||||||
|
@remove.command(name='admin_role', aliases=['admin'])
|
||||||
|
@commands.cooldown(1, 5, type=commands.BucketType.guild)
|
||||||
|
@commands.check(checks.is_guild_owner)
|
||||||
|
async def _remove_admin_role(self, ctx, role=None):
|
||||||
|
role = discord.utils.get(ctx.guild.roles, name=role)
|
||||||
|
if role is not None:
|
||||||
|
roles = json.loads(self.bot.con.one('select admin_roles from guild_config where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id}))
|
||||||
|
if role.name in roles:
|
||||||
|
del roles[role.name]
|
||||||
|
self.bot.con.run('update guild_config set admin_roles = %(roles)s where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id, 'roles': roles})
|
||||||
|
await ctx.send(f'{role.name} has been removed from the list of admin roles for this guild.')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'{role.name} is not registered as an admin role in this guild.')
|
||||||
|
else:
|
||||||
|
await ctx.send('You must include a role with this command.')
|
||||||
|
|
||||||
|
|
||||||
|
def setup(bot):
|
||||||
|
bot.add_cog(Admin(bot))
|
||||||
+279
@@ -0,0 +1,279 @@
|
|||||||
|
import discord
|
||||||
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
from .imports import utils
|
||||||
|
|
||||||
|
config_dir = 'config/'
|
||||||
|
admin_id_file = 'admin_ids'
|
||||||
|
extension_dir = 'extensions'
|
||||||
|
owner_id = 351794468870946827
|
||||||
|
guild_config_dir = 'guild_config/'
|
||||||
|
rcon_config_file = 'server_rcon_config'
|
||||||
|
dododex_url = 'http://www.dododex.com'
|
||||||
|
embed_color = discord.Colour.from_rgb(49, 107, 111)
|
||||||
|
red_color = discord.Colour.from_rgb(142, 29, 31)
|
||||||
|
bot_config_file = 'bot_config'
|
||||||
|
default_guild_config_file = 'default_guild_config.json'
|
||||||
|
|
||||||
|
events_log = logging.getLogger('events')
|
||||||
|
|
||||||
|
emojis = {
|
||||||
|
'x': '❌',
|
||||||
|
'y': '✅',
|
||||||
|
'poop': '💩',
|
||||||
|
'crown': '👑',
|
||||||
|
'eggplant': '🍆',
|
||||||
|
'sob': '😭',
|
||||||
|
'trident': '🔱'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class BotEvents:
|
||||||
|
def __init__(self, bot):
|
||||||
|
self.bot = bot
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_config_string(guild_config):
|
||||||
|
config_str = ''
|
||||||
|
for config in guild_config:
|
||||||
|
if isinstance(guild_config[config], dict):
|
||||||
|
config_str = f'{config_str}\n{" "*4}{config}'
|
||||||
|
for item in guild_config[config]:
|
||||||
|
config_str = f'{config_str}\n{" "*8}{item}: {guild_config[config][item]}'
|
||||||
|
elif isinstance(guild_config[config], list):
|
||||||
|
config_str = f'{config_str}\n{" "*4}{config}'
|
||||||
|
for item in guild_config[config]:
|
||||||
|
config_str = f'{config_str}\n{" "*8}{item}'
|
||||||
|
else:
|
||||||
|
config_str = f'{config_str}\n{" "*4}{config}: {guild_config[config]}'
|
||||||
|
return config_str
|
||||||
|
|
||||||
|
async def on_raw_message_delete(self, msg_id, chan_id):
|
||||||
|
self.bot.con.run('update messages set deleted_at = %(time)s where id = %(id)s',
|
||||||
|
{'time': datetime.utcnow(), 'id': msg_id})
|
||||||
|
|
||||||
|
async def on_raw_bulk_message_delete(self, msg_ids, chan_id):
|
||||||
|
sql = ''
|
||||||
|
for msg_id in msg_ids:
|
||||||
|
sql += f';update messages set deleted_at = %(time)s where id = {msg_id}'
|
||||||
|
self.bot.con.run(sql, {'time': datetime.utcnow()})
|
||||||
|
|
||||||
|
async def on_message(self, ctx):
|
||||||
|
try:
|
||||||
|
if ctx.author in self.bot.infected:
|
||||||
|
if datetime.now().timestamp() > self.bot.infected[ctx.author][1] + 300:
|
||||||
|
del self.bot.infected[ctx.author]
|
||||||
|
# await ctx.channel.send(f'{ctx.author.mention} You have been healed.')
|
||||||
|
else:
|
||||||
|
await ctx.add_reaction(self.bot.infected[ctx.author][0])
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
sql = 'insert into messages (id, tts, type, content, embeds, channel, mention_everyone, mentions,\
|
||||||
|
channel_mentions, role_mentions, webhook, attachments, pinned, reactions, guild, created_at,\
|
||||||
|
system_content, author) \
|
||||||
|
values (%(id)s, %(tts)s, %(type)s, %(content)s, %(embeds)s, %(channel)s, %(mention_everyone)s, %(mentions)s,\
|
||||||
|
%(channel_mentions)s, %(role_mentions)s, %(webhook)s, %(attachments)s, %(pinned)s, %(reactions)s, %(guild)s,\
|
||||||
|
%(created_at)s, %(system_content)s, %(author)s)'
|
||||||
|
msg_data = dict()
|
||||||
|
msg_data['id'] = ctx.id
|
||||||
|
msg_data['tts'] = ctx.tts
|
||||||
|
msg_data['type'] = str(ctx.type)
|
||||||
|
msg_data['content'] = ctx.content
|
||||||
|
msg_data['embeds'] = [json.dumps(e.to_dict()) for e in ctx.embeds]
|
||||||
|
msg_data['channel'] = ctx.channel.id
|
||||||
|
msg_data['mention_everyone'] = ctx.mention_everyone
|
||||||
|
msg_data['mentions'] = [user.id for user in ctx.mentions]
|
||||||
|
msg_data['channel_mentions'] = [channel.id for channel in ctx.channel_mentions]
|
||||||
|
msg_data['role_mentions'] = [role.id for role in ctx.role_mentions]
|
||||||
|
msg_data['webhook'] = ctx.webhook_id
|
||||||
|
msg_data['attachments'] = [json.dumps({'id': a.id, 'size': a.size, 'height': a.height, 'width': a.width,
|
||||||
|
'filename': a.filename, 'url': a.url}) for a in ctx.attachments]
|
||||||
|
msg_data['pinned'] = ctx.pinned
|
||||||
|
msg_data['guild'] = ctx.guild.id
|
||||||
|
msg_data['created_at'] = ctx.created_at
|
||||||
|
msg_data['system_content'] = ctx.system_content
|
||||||
|
msg_data['author'] = ctx.author.id
|
||||||
|
msg_data['reactions'] = [json.dumps({'emoji': r.emoji, 'count': r.count}) for r in ctx.reactions]
|
||||||
|
self.bot.con.run(sql, msg_data)
|
||||||
|
if ctx.guild:
|
||||||
|
if ctx.author != ctx.guild.me:
|
||||||
|
if self.bot.con.one(f"select pg_filter from guild_config where guild_id = {ctx.guild.id}"):
|
||||||
|
profane = 0
|
||||||
|
for word in self.bot.con.one('select profane_words from guild_config where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id}):
|
||||||
|
word = word.strip()
|
||||||
|
if word in ctx.content.lower():
|
||||||
|
events_log.info(f'Found non PG word {word}')
|
||||||
|
repl_str = '\*' * (len(word) - 1)
|
||||||
|
re_replace = re.compile(re.escape(word), re.IGNORECASE)
|
||||||
|
ctx.content = re_replace.sub(f'{word[:1]}{repl_str}', ctx.content)
|
||||||
|
profane = 1
|
||||||
|
if profane:
|
||||||
|
hook = await ctx.channel.create_webhook(name="PG Filter")
|
||||||
|
await ctx.delete()
|
||||||
|
if len(ctx.author.display_name) < 2:
|
||||||
|
username = f'឵{ctx.author.display_name}'
|
||||||
|
else:
|
||||||
|
username = ctx.author.display_name
|
||||||
|
await hook.send(ctx.content, username=username, avatar_url=ctx.author.avatar_url)
|
||||||
|
await hook.delete()
|
||||||
|
|
||||||
|
async def on_reaction_add(self, react, user):
|
||||||
|
if react.emoji == emojis['poop'] and react.message.author.id == 351794468870946827:
|
||||||
|
await react.message.remove_reaction(emojis['poop'], user)
|
||||||
|
await react.message.channel.send(f"You can't Poop on my Owner {user.mention} :P")
|
||||||
|
if react.emoji == emojis['poop'] and react.message.author.id == 396588996706304010:
|
||||||
|
await react.message.remove_reaction(emojis['poop'], user)
|
||||||
|
await react.message.channel.send(f"You can't Poop on me {user.mention} :P")
|
||||||
|
reactions = react.message.reactions
|
||||||
|
reacts = [json.dumps({'emoji': r.emoji, 'count': r.count}) for r in reactions]
|
||||||
|
self.bot.con.run('update messages set reactions = %(reacts)s where id = %(id)s',
|
||||||
|
{'id': react.message.id, 'reacts': reacts})
|
||||||
|
|
||||||
|
async def on_message_edit(self, before, ctx):
|
||||||
|
previous_content = self.bot.con.one('select previous_content from messages where id = %(id)s', {'id': ctx.id})
|
||||||
|
if previous_content:
|
||||||
|
previous_content.append(before.content)
|
||||||
|
else:
|
||||||
|
previous_content = [before.content]
|
||||||
|
previous_embeds = self.bot.con.one('select previous_embeds from messages where id = %(id)s', {'id': ctx.id})
|
||||||
|
if previous_embeds:
|
||||||
|
previous_embeds.append([json.dumps(e.to_dict()) for e in before.embeds])
|
||||||
|
else:
|
||||||
|
previous_embeds = [[json.dumps(e.to_dict()) for e in before.embeds]]
|
||||||
|
sql = 'update messages set (edited_at, previous_content, previous_embeds, tts, type, content,\
|
||||||
|
embeds, channel, mention_everyone, mentions, channel_mentions, role_mentions, webhook,\
|
||||||
|
attachments, pinned, reactions, guild, created_at, system_content, author) \
|
||||||
|
= (%(edited_at)s, %(previous_content)s, %(previous_embeds)s, %(tts)s, %(type)s, %(content)s,\
|
||||||
|
%(embeds)s, %(channel)s, %(mention_everyone)s, %(mentions)s, %(channel_mentions)s, %(role_mentions)s,\
|
||||||
|
%(webhook)s, %(attachments)s, %(pinned)s, %(reactions)s, %(guild)s, %(created_at)s, %(system_content)s,\
|
||||||
|
%(author)s) where id = %(id)s'
|
||||||
|
msg_data = dict()
|
||||||
|
msg_data['id'] = ctx.id
|
||||||
|
msg_data['tts'] = ctx.tts
|
||||||
|
msg_data['type'] = str(ctx.type)
|
||||||
|
msg_data['content'] = ctx.content
|
||||||
|
msg_data['embeds'] = [json.dumps(e.to_dict()) for e in ctx.embeds]
|
||||||
|
msg_data['channel'] = ctx.channel.id
|
||||||
|
msg_data['mention_everyone'] = ctx.mention_everyone
|
||||||
|
msg_data['mentions'] = [user.id for user in ctx.mentions]
|
||||||
|
msg_data['channel_mentions'] = [channel.id for channel in ctx.channel_mentions]
|
||||||
|
msg_data['role_mentions'] = [role.id for role in ctx.role_mentions]
|
||||||
|
msg_data['webhook'] = ctx.webhook_id
|
||||||
|
msg_data['attachments'] = ctx.attachments
|
||||||
|
msg_data['pinned'] = ctx.pinned
|
||||||
|
msg_data['guild'] = ctx.guild.id
|
||||||
|
msg_data['created_at'] = ctx.created_at
|
||||||
|
msg_data['system_content'] = ctx.system_content
|
||||||
|
msg_data['author'] = ctx.author.id
|
||||||
|
msg_data['reactions'] = [json.dumps({'emoji': r.emoji, 'count': r.count}) for r in ctx.reactions]
|
||||||
|
msg_data['previous_content'] = previous_content
|
||||||
|
msg_data['previous_embeds'] = previous_embeds
|
||||||
|
msg_data['edited_at'] = datetime.utcnow()
|
||||||
|
self.bot.con.run(sql, msg_data)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def on_command_error(ctx, error):
|
||||||
|
if ctx.channel.id == 418452585683484680 and type(error) == discord.ext.commands.errors.CommandNotFound:
|
||||||
|
return
|
||||||
|
for page in utils.paginate(error):
|
||||||
|
await ctx.send(page)
|
||||||
|
|
||||||
|
async def on_guild_join(self, guild):
|
||||||
|
with open(f"{config_dir}{default_guild_config_file}", 'r') as file:
|
||||||
|
default_config = json.loads(file.read())
|
||||||
|
admin_role = guild.role_hierarchy[0]
|
||||||
|
default_config['admin_roles'] = {admin_role.name: admin_role.id}
|
||||||
|
default_config['name'] = guild.name.replace("'", "\\'")
|
||||||
|
default_config['guild_id'] = guild.id
|
||||||
|
events_log.info(default_config)
|
||||||
|
self.bot.con.run("insert into guild_config(guild_id, guild_name, admin_roles, rcon_enabled, channel_lockdown,\
|
||||||
|
raid_status, pg_filter, patreon_enabled, referral_enabled)\
|
||||||
|
values (%(guild_id)s, %(name)s, %(admin_roles)s, %(rcon_enabled)s, %(channel_lockdown)s,\
|
||||||
|
%(raid_status)s, %(pg_filter)s, %(patreon_enabled)s, %(referral_enabled)s)",
|
||||||
|
{'guild_id': default_config['guild_id'],
|
||||||
|
'name': default_config['name'],
|
||||||
|
'admin_roles': json.dumps(default_config['admin_roles']),
|
||||||
|
'rcon_enabled': default_config['rcon_enabled'],
|
||||||
|
'channel_lockdown': default_config['channel_lockdown'],
|
||||||
|
'raid_status': default_config['raid_status'],
|
||||||
|
'pg_filter': default_config['pg_filter'],
|
||||||
|
'patreon_enabled': default_config['patreon_enabled'],
|
||||||
|
'referral_enabled': default_config['referral_enabled']
|
||||||
|
})
|
||||||
|
events_log.info(f'Entry Created for {guild.name}')
|
||||||
|
await guild.me.edit(nick='[g$] Geeksbot')
|
||||||
|
|
||||||
|
async def on_guild_remove(self, guild):
|
||||||
|
self.bot.con.run(f'delete from guild_config where guild_id = %(id)s', {'id': guild.id})
|
||||||
|
events_log.info(f'Left the {guild.name} guild.')
|
||||||
|
|
||||||
|
async def on_member_join(self, member):
|
||||||
|
events_log.info(f'Member joined: {member.name} {member.id} Guild: {member.guild.name} {member.guild.id}')
|
||||||
|
join_chan = self.bot.con.one('select join_leave_chat from guild_config where guild_id = %(id)s',
|
||||||
|
{'id': member.guild.id})
|
||||||
|
if join_chan:
|
||||||
|
em = discord.Embed(style='rich',
|
||||||
|
color=embed_color
|
||||||
|
)
|
||||||
|
em.set_thumbnail(url=member.avatar_url)
|
||||||
|
em.add_field(name=f'Welcome {member.name}#{member.discriminator}', value=member.id, inline=False)
|
||||||
|
em.add_field(name='User created on:', value=member.created_at.strftime('%Y-%m-%d at %H:%M:%S GMT'),
|
||||||
|
inline=True)
|
||||||
|
em.add_field(name='Bot:', value=str(member.bot))
|
||||||
|
em.set_footer(text=f"{member.guild.name} | {member.joined_at.strftime('%Y-%m-%d at %H:%M:%S GMT')}",
|
||||||
|
icon_url=member.guild.icon_url)
|
||||||
|
await discord.utils.get(member.guild.channels, id=join_chan).send(embed=em)
|
||||||
|
mem_data = {'id': member.id,
|
||||||
|
'name': member.name,
|
||||||
|
'discriminator': member.discriminator,
|
||||||
|
'bot': member.bot
|
||||||
|
}
|
||||||
|
mem = self.bot.con.one('select guilds,nicks from user_data where id = %(id)s', {'id': member.id})
|
||||||
|
if mem:
|
||||||
|
mem[1].append(json.dumps({member.guild.id: member.display_name}))
|
||||||
|
mem[0].append(member.guild.id)
|
||||||
|
mem_data['nicks'] = mem[1]
|
||||||
|
mem_data['guilds'] = mem[0]
|
||||||
|
self.bot.con.run('update user_data set (name, discriminator, bot, nicks, guilds) =\
|
||||||
|
(%(name)s, %(discriminator)s, %(bot)s, %(nicks)s, %(guilds)s) where\
|
||||||
|
id = %(id)s', mem_data)
|
||||||
|
else:
|
||||||
|
mem_data['nicks'] = [json.dumps({member.guild.id: member.display_name})]
|
||||||
|
mem_data['guilds'] = [member.guild.id]
|
||||||
|
self.bot.con.run('insert into user_data (id, name, discriminator, bot, nicks, guilds) values\
|
||||||
|
(%(id)s, %(name)s, %(discriminator)s, %(bot)s, %(nicks)s, %(guilds)s)', mem_data)
|
||||||
|
|
||||||
|
async def on_member_remove(self, member):
|
||||||
|
leave_time = datetime.utcnow()
|
||||||
|
events_log.info(f'Member left: {member.name} {member.id} Guild: {member.guild.name} {member.guild.id}')
|
||||||
|
join_chan = self.bot.con.one('select join_leave_chat from guild_config where guild_id = %(id)s',
|
||||||
|
{'id': member.guild.id})
|
||||||
|
if join_chan:
|
||||||
|
em = discord.Embed(style='rich',
|
||||||
|
color=red_color
|
||||||
|
)
|
||||||
|
em.set_thumbnail(url=member.avatar_url)
|
||||||
|
em.add_field(name=f'RIP {member.name}#{member.discriminator}', value=member.id, inline=False)
|
||||||
|
join_time = member.joined_at
|
||||||
|
em.add_field(name='Joined on:', value=join_time.strftime('%Y-%m-%d at %H:%M:%S GMT'), inline=True)
|
||||||
|
em.add_field(name='Bot:', value=str(member.bot), inline=True)
|
||||||
|
em.add_field(name='Left on:', value=leave_time.strftime('%Y-%m-%d at %H:%M:%S GMT'), inline=False)
|
||||||
|
total_time = leave_time - join_time
|
||||||
|
days, remainder = divmod(total_time.total_seconds(), 86400)
|
||||||
|
hours, remainder = divmod(remainder, 3600)
|
||||||
|
minutes, seconds = divmod(remainder, 60)
|
||||||
|
days_str = str(int(days)) + ' days, ' if days != 0 else ''
|
||||||
|
hours_str = str(int(hours)) + ' hours, ' if hours != 0 else ''
|
||||||
|
minutes_str = str(int(minutes)) + ' minutes and ' if minutes != 0 else ''
|
||||||
|
time_str = f"{days_str}{hours_str}{minutes_str}{int(seconds)} seconds"
|
||||||
|
em.add_field(name='Total time in Guild:', value=time_str, inline=False)
|
||||||
|
em.set_footer(text=f"{member.guild.name} | {datetime.utcnow().strftime('%Y-%m-%d at %H:%M:%S GMT')}",
|
||||||
|
icon_url=member.guild.icon_url)
|
||||||
|
await discord.utils.get(member.guild.channels, id=join_chan).send(embed=em)
|
||||||
|
|
||||||
|
|
||||||
|
def setup(bot):
|
||||||
|
bot.add_cog(BotEvents(bot))
|
||||||
+151
@@ -0,0 +1,151 @@
|
|||||||
|
import discord
|
||||||
|
from discord.ext import commands
|
||||||
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
import asyncio
|
||||||
|
import youtube_dl
|
||||||
|
|
||||||
|
config_dir = 'config/'
|
||||||
|
admin_id_file = 'admin_ids'
|
||||||
|
extension_dir = 'extensions'
|
||||||
|
owner_id = 351794468870946827
|
||||||
|
guild_config_dir = 'guild_config/'
|
||||||
|
rcon_config_file = 'server_rcon_config'
|
||||||
|
dododex_url = 'http://www.dododex.com'
|
||||||
|
embed_color = discord.Colour.from_rgb(49, 107, 111)
|
||||||
|
bot_config_file = 'bot_config'
|
||||||
|
default_guild_config_file = 'default_guild_config.json'
|
||||||
|
emoji_guild = 408524303164899338
|
||||||
|
|
||||||
|
events_log = logging.getLogger('events')
|
||||||
|
|
||||||
|
emojis = {
|
||||||
|
'x': '❌',
|
||||||
|
'y': '✅',
|
||||||
|
'poop': '💩'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Fun:
|
||||||
|
def __init__(self, bot):
|
||||||
|
self.bot = bot
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
@commands.cooldown(1, 30, type=commands.BucketType.user)
|
||||||
|
async def infect(self, ctx, member: discord.Member, emoji):
|
||||||
|
if member.id == self.bot.user.id and ctx.author.id != owner_id:
|
||||||
|
await ctx.send(f'You rolled a Critical Fail...\nInfection bounces off and rebounds on the attacker.')
|
||||||
|
member = ctx.author
|
||||||
|
if member in self.bot.infected:
|
||||||
|
await ctx.send(f'{member.display_name} is already infected. '
|
||||||
|
f'Please wait until they are healed before infecting them again...')
|
||||||
|
else:
|
||||||
|
emoji = self.bot.get_emoji(int(emoji.split(':')[2].strip('>'))) if '<:' in emoji \
|
||||||
|
or '<a:' in emoji else emoji
|
||||||
|
self.bot.infected[member] = [emoji, datetime.now().timestamp()]
|
||||||
|
await ctx.send(f"{member.display_name} has been infected with {emoji}")
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
@commands.cooldown(1, 5, type=commands.BucketType.user)
|
||||||
|
async def heal(self, ctx, member: discord.Member):
|
||||||
|
if ctx.author == member and ctx.author.id != owner_id:
|
||||||
|
await ctx.send('You can\'t heal yourself silly...')
|
||||||
|
else:
|
||||||
|
if member in self.bot.infected:
|
||||||
|
del self.bot.infected[member]
|
||||||
|
await ctx.send(f'{member.mention} You have been healed by {ctx.author.display_name}.')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'{member.display_name} is not infected...')
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
@commands.is_owner()
|
||||||
|
async def print_infections(self, ctx):
|
||||||
|
await ctx.author.send(f'```{self.bot.infected}```')
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
@commands.cooldown(1, 5, type=commands.BucketType.user)
|
||||||
|
async def slap(self, ctx, member: discord.Member):
|
||||||
|
if member.id == self.bot.user.id and ctx.author.id != owner_id:
|
||||||
|
await ctx.send(f'You rolled a Critical Fail...\nThe trout bounces off and rebounds on the attacker.')
|
||||||
|
await ctx.send(f'{ctx.author.mention} '
|
||||||
|
f'You slap yourself in the face with a large trout <:trout:408543365085397013>')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'{ctx.author.display_name} slaps '
|
||||||
|
f'{member.mention} around a bit with a large trout <:trout:408543365085397013>')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_factorial(number):
|
||||||
|
a = 1
|
||||||
|
for i in range(1, int(number)):
|
||||||
|
a = a * (i + 1)
|
||||||
|
return a
|
||||||
|
|
||||||
|
# @commands.command()
|
||||||
|
# @commands.cooldown(1, 5, type=commands.BucketType.user)
|
||||||
|
# async def fact(self, ctx, number:int):
|
||||||
|
# if number < 20001 and number > 0:
|
||||||
|
# n = 1990
|
||||||
|
# with ctx.channel.typing():
|
||||||
|
# a = await self.bot.loop.run_in_executor(None, self.get_factorial, number)
|
||||||
|
# if len(str(a)) > 6000:
|
||||||
|
# for b in [str(a)[i:i+n] for i in range(0, len(str(a)), n)]:
|
||||||
|
# await ctx.author.send(f'```py\n{b}```')
|
||||||
|
# await ctx.send(f"{ctx.author.mention} Check your DMs.")
|
||||||
|
# else:
|
||||||
|
# for b in [str(a)[i:i+n] for i in range(0, len(str(a)), n)]:
|
||||||
|
# await ctx.send(f'```py\n{b}```')
|
||||||
|
# else:
|
||||||
|
# await ctx.send("Invalid number. Please enter a number between 0 and 20,000")
|
||||||
|
|
||||||
|
@commands.command(hidden=True)
|
||||||
|
@commands.is_owner()
|
||||||
|
async def play(self, ctx, url=None):
|
||||||
|
if ctx.author.voice.channel.name in self.bot.voice_chans:
|
||||||
|
if self.bot.voice_chans[ctx.author.voice.channel.name].is_playing():
|
||||||
|
await ctx.send('There is currently a song playing. Please wait until it is done...')
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.bot.voice_chans[ctx.author.voice.channel.name] = await ctx.author.voice.channel.connect()
|
||||||
|
asyncio.sleep(5)
|
||||||
|
if url:
|
||||||
|
opts = {"format": 'webm[abr>0]/bestaudio/best',
|
||||||
|
"ignoreerrors": True,
|
||||||
|
"default_search": "auto",
|
||||||
|
"source_address": "0.0.0.0",
|
||||||
|
'quiet': True}
|
||||||
|
ydl = youtube_dl.YoutubeDL(opts)
|
||||||
|
info = ydl.extract_info(url, download=False)
|
||||||
|
self.bot.player = discord.FFmpegPCMAudio(info['url'])
|
||||||
|
else:
|
||||||
|
self.bot.player = discord.FFmpegPCMAudio('dead_puppies.mp3')
|
||||||
|
self.bot.player = discord.PCMVolumeTransformer(self.bot.player, volume=0.3)
|
||||||
|
self.bot.voice_chans[ctx.author.voice.channel.name].play(self.bot.player)
|
||||||
|
|
||||||
|
@commands.command(hidden=True)
|
||||||
|
@commands.is_owner()
|
||||||
|
async def stop(self, ctx):
|
||||||
|
if ctx.author.voice.channel.name in self.bot.voice_chans:
|
||||||
|
if self.bot.voice_chans[ctx.author.voice.channel.name].is_playing():
|
||||||
|
self.bot.voice_chans[ctx.author.voice.channel.name].stop()
|
||||||
|
else:
|
||||||
|
await ctx.send('Nothing is playing...')
|
||||||
|
else:
|
||||||
|
await ctx.send('Not connected to that voice channel.')
|
||||||
|
|
||||||
|
@commands.command(hidden=True)
|
||||||
|
@commands.is_owner()
|
||||||
|
async def disconnect(self, ctx):
|
||||||
|
if ctx.author.voice.channel.name in self.bot.voice_chans:
|
||||||
|
await self.bot.voice_chans[ctx.author.voice.channel.name].disconnect()
|
||||||
|
del self.bot.voice_chans[ctx.author.voice.channel.name]
|
||||||
|
else:
|
||||||
|
await ctx.send('Not connected to that voice channel.')
|
||||||
|
|
||||||
|
@commands.command(hidden=True)
|
||||||
|
@commands.is_owner()
|
||||||
|
async def volume(self, ctx, volume: float):
|
||||||
|
self.bot.player.volume = volume
|
||||||
|
|
||||||
|
|
||||||
|
def setup(bot):
|
||||||
|
bot.add_cog(Fun(bot))
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
import discord
|
||||||
|
from discord.ext import commands
|
||||||
|
import logging
|
||||||
|
from .imports.utils import paginate, run_command
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
owner_id = 351794468870946827
|
||||||
|
embed_color = discord.Colour.from_rgb(49, 107, 111)
|
||||||
|
|
||||||
|
git_log = logging.getLogger('git')
|
||||||
|
|
||||||
|
|
||||||
|
class Git:
|
||||||
|
def __init__(self, bot):
|
||||||
|
self.bot = bot
|
||||||
|
|
||||||
|
@commands.group(case_insensitive=True)
|
||||||
|
async def git(self, ctx):
|
||||||
|
"""Run help git for more info"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@git.command()
|
||||||
|
@commands.is_owner()
|
||||||
|
async def pull(self, ctx):
|
||||||
|
em = discord.Embed(style='rich',
|
||||||
|
title=f'Git Pull',
|
||||||
|
color=embed_color)
|
||||||
|
em.set_thumbnail(url=f'{ctx.guild.me.avatar_url}')
|
||||||
|
result = await asyncio.wait_for(self.bot.loop.create_task(run_command('git fetch --all')), 120) + '\n'
|
||||||
|
result += await asyncio.wait_for(self.bot.loop.create_task(run_command('git reset --hard '
|
||||||
|
'origin/master')), 120) + '\n\n'
|
||||||
|
result += await asyncio.wait_for(self.bot.loop.create_task(run_command('git show --stat | '
|
||||||
|
'sed "s/.*@.*[.].*/ /g"')), 10)
|
||||||
|
results = paginate(result, maxlen=1014)
|
||||||
|
for page in results[:5]:
|
||||||
|
em.add_field(name='', value=f'{page}')
|
||||||
|
await ctx.send(embed=em)
|
||||||
|
|
||||||
|
@git.command()
|
||||||
|
@commands.is_owner()
|
||||||
|
async def status(self, ctx):
|
||||||
|
em = discord.Embed(style='rich',
|
||||||
|
title=f'Git Pull',
|
||||||
|
color=embed_color)
|
||||||
|
em.set_thumbnail(url=f'{ctx.guild.me.avatar_url}')
|
||||||
|
result = await asyncio.wait_for(self.bot.loop.create_task(run_command('git status')), 10)
|
||||||
|
results = paginate(result, maxlen=1014)
|
||||||
|
for page in results[:5]:
|
||||||
|
em.add_field(name='', value=f'{page}')
|
||||||
|
await ctx.send(embed=em)
|
||||||
|
|
||||||
|
|
||||||
|
def setup(bot):
|
||||||
|
bot.add_cog(Git(bot))
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import discord
|
||||||
|
import json
|
||||||
|
from . import utils
|
||||||
|
|
||||||
|
owner_id = 351794468870946827
|
||||||
|
|
||||||
|
|
||||||
|
def check_admin_role(bot, ctx, member):
|
||||||
|
admin_roles = json.loads(bot.con.one(f"select admin_roles from guild_config where guild_id = %(id)s",
|
||||||
|
{'id': ctx.guild.id}))
|
||||||
|
for role in admin_roles:
|
||||||
|
if discord.utils.get(ctx.guild.roles, id=admin_roles[role]) in member.roles:
|
||||||
|
return True
|
||||||
|
return member.id == ctx.guild.owner.id or member.id == owner_id
|
||||||
|
|
||||||
|
|
||||||
|
def check_rcon_role(bot, ctx, member):
|
||||||
|
rcon_admin_roles = json.loads(bot.con.one("select rcon_admin_roles from guild_config where guild_id = %(id)s",
|
||||||
|
{'id': ctx.guild.id}))
|
||||||
|
for role in rcon_admin_roles:
|
||||||
|
if discord.utils.get(ctx.guild.roles, id=rcon_admin_roles[role]) in member.roles:
|
||||||
|
return True
|
||||||
|
return member.id == ctx.guild.owner.id or member.id == owner_id
|
||||||
|
|
||||||
|
|
||||||
|
def is_admin(bot, ctx):
|
||||||
|
admin_roles = json.loads(bot.con.one("select admin_roles from guild_config where guild_id = %(id)s",
|
||||||
|
{'id': ctx.guild.id}))
|
||||||
|
for role in admin_roles:
|
||||||
|
if discord.utils.get(ctx.guild.roles, id=admin_roles[role]) in ctx.message.author.roles:
|
||||||
|
return True
|
||||||
|
return ctx.message.author.id == ctx.guild.owner.id or ctx.message.author.id == owner_id
|
||||||
|
|
||||||
|
|
||||||
|
def is_guild_owner(ctx):
|
||||||
|
if ctx.guild:
|
||||||
|
return ctx.message.author.id == ctx.guild.owner.id or ctx.message.author.id == owner_id
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def is_rcon_admin(bot, ctx):
|
||||||
|
rcon_admin_roles = json.loads(bot.con.one("select rcon_admin_roles from guild_config where guild_id = %(id)s",
|
||||||
|
{'id': ctx.guild.id}))
|
||||||
|
for role in rcon_admin_roles:
|
||||||
|
if discord.utils.get(ctx.guild.roles, id=rcon_admin_roles[role]) in ctx.message.author.roles:
|
||||||
|
return True
|
||||||
|
return ctx.message.author.id == ctx.guild.owner.id or ctx.message.author.id == owner_id
|
||||||
|
|
||||||
|
|
||||||
|
def is_restricted_chan(ctx):
|
||||||
|
if ctx.guild:
|
||||||
|
if ctx.channel.overwrites_for(ctx.guild.default_role).read_messages is False:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
async def is_spam(bot, ctx):
|
||||||
|
max_rep = 5
|
||||||
|
rep_time = 20
|
||||||
|
spam_rep = 5
|
||||||
|
spam_time = 3
|
||||||
|
spam_check = 0
|
||||||
|
msg_check = 0
|
||||||
|
for msg in bot.recent_msgs[ctx.guild.id]:
|
||||||
|
if msg['author'] == ctx.author:
|
||||||
|
if msg['time'] > ctx.message.created_at.timestamp() - spam_time:
|
||||||
|
spam_check += 1
|
||||||
|
if msg['content'].lower() == ctx.content.lower() \
|
||||||
|
and msg['time'] > ctx.message.created_at.timestamp() - rep_time:
|
||||||
|
msg_check += 1
|
||||||
|
if spam_check == spam_rep - 1 or msg_check == max_rep - 1:
|
||||||
|
await utils.mute(bot, ctx, admin=1, member=ctx.author.id)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
from io import StringIO
|
||||||
|
import sys
|
||||||
|
import asyncio
|
||||||
|
import discord
|
||||||
|
from discord.ext.commands.formatter import Paginator
|
||||||
|
from . import checks
|
||||||
|
|
||||||
|
|
||||||
|
class Capturing(list):
|
||||||
|
def __enter__(self):
|
||||||
|
self._stdout = sys.stdout
|
||||||
|
sys.stdout = self._stringio = StringIO()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, *args):
|
||||||
|
self.extend(self._stringio.getvalue().splitlines())
|
||||||
|
del self._stringio # free up some memory
|
||||||
|
sys.stdout = self._stdout
|
||||||
|
|
||||||
|
|
||||||
|
async def mute(bot, ctx, admin=0, member_id=None):
|
||||||
|
mute_role = bot.con.one(f'select muted_role from guild_config where guild_id = {ctx.guild.id}')
|
||||||
|
if mute_role:
|
||||||
|
if admin or checks.is_admin(bot, ctx):
|
||||||
|
if ctx.guild.me.guild_permissions.manage_roles:
|
||||||
|
if member_id:
|
||||||
|
ctx.guild.get_member(member_id).edit(roles=[discord.utils.get(ctx.guild.roles, id=mute_role)])
|
||||||
|
|
||||||
|
|
||||||
|
def to_list_of_str(items, out: list=list(), level=1, recurse=0):
|
||||||
|
def rec_loop(item, key, out, level):
|
||||||
|
quote = '"'
|
||||||
|
if type(item) == list:
|
||||||
|
out.append(f'{" "*level}{quote+key+quote+": " if key else ""}[')
|
||||||
|
new_level = level + 1
|
||||||
|
out = to_list_of_str(item, out, new_level, 1)
|
||||||
|
out.append(f'{" "*level}]')
|
||||||
|
elif type(item) == dict:
|
||||||
|
out.append(f'{" "*level}{quote+key+quote+": " if key else ""}{{')
|
||||||
|
new_level = level + 1
|
||||||
|
out = to_list_of_str(item, out, new_level, 1)
|
||||||
|
out.append(f'{" "*level}}}')
|
||||||
|
else:
|
||||||
|
out.append(f'{" "*level}{quote+key+quote+": " if key else ""}{repr(item)},')
|
||||||
|
|
||||||
|
if type(items) == list:
|
||||||
|
if not recurse:
|
||||||
|
out = list()
|
||||||
|
out.append('[')
|
||||||
|
for item in items:
|
||||||
|
rec_loop(item, None, out, level)
|
||||||
|
if not recurse:
|
||||||
|
out.append(']')
|
||||||
|
elif type(items) == dict:
|
||||||
|
if not recurse:
|
||||||
|
out = list()
|
||||||
|
out.append('{')
|
||||||
|
for key in items:
|
||||||
|
rec_loop(items[key], key, out, level)
|
||||||
|
if not recurse:
|
||||||
|
out.append('}')
|
||||||
|
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def paginate(text, maxlen=1990):
|
||||||
|
paginator = Paginator(prefix='```py', max_size=maxlen+10)
|
||||||
|
if type(text) == list:
|
||||||
|
data = to_list_of_str(text)
|
||||||
|
elif type(text) == dict:
|
||||||
|
data = to_list_of_str(text)
|
||||||
|
else:
|
||||||
|
data = str(text).split('\n')
|
||||||
|
for line in data:
|
||||||
|
if len(line) > maxlen:
|
||||||
|
n = maxlen
|
||||||
|
for l in [line[i:i+n] for i in range(0, len(line), n)]:
|
||||||
|
paginator.add_line(l)
|
||||||
|
else:
|
||||||
|
paginator.add_line(line)
|
||||||
|
return paginator.pages
|
||||||
|
|
||||||
|
|
||||||
|
async def run_command(args):
|
||||||
|
# Create subprocess
|
||||||
|
process = await asyncio.create_subprocess_shell(
|
||||||
|
args,
|
||||||
|
# stdout must a pipe to be accessible as process.stdout
|
||||||
|
stdout=asyncio.subprocess.PIPE)
|
||||||
|
# Wait for the subprocess to finish
|
||||||
|
stdout, stderr = await process.communicate()
|
||||||
|
# Return stdout
|
||||||
|
return stdout.decode().strip()
|
||||||
+126
@@ -0,0 +1,126 @@
|
|||||||
|
import discord
|
||||||
|
from discord.ext import commands
|
||||||
|
import json
|
||||||
|
from .imports import checks
|
||||||
|
|
||||||
|
config_dir = 'config'
|
||||||
|
extension_dir = 'extensions'
|
||||||
|
owner_id = 351794468870946827
|
||||||
|
|
||||||
|
|
||||||
|
class Patreon:
|
||||||
|
|
||||||
|
def __init__(self, bot):
|
||||||
|
self.bot = bot
|
||||||
|
|
||||||
|
@commands.command(aliases=['get_patreon', 'patreon'])
|
||||||
|
@commands.cooldown(1, 5, type=commands.BucketType.user)
|
||||||
|
async def get_patreon_links(self, ctx, target: discord.Member=None):
|
||||||
|
"""Prints Patreon information for creators on the server."""
|
||||||
|
if self.bot.con.one('select patreon_enabled from guild_config where guild_id = %(id)s', {'id': ctx.guild.id}):
|
||||||
|
patreon_info = self.bot.con.one('select patreon_message,patreon_links\
|
||||||
|
from guild_config where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id})
|
||||||
|
message = patreon_info[0].replace('\\n', '\n')
|
||||||
|
patreon_links = json.loads(patreon_info[1])
|
||||||
|
for key in patreon_links:
|
||||||
|
message = message + '\n{0}: {1}'.format(key, patreon_links[key])
|
||||||
|
if target is None:
|
||||||
|
await ctx.send(message)
|
||||||
|
else:
|
||||||
|
await ctx.send('{0}\n{1}'.format(target.mention, message))
|
||||||
|
else:
|
||||||
|
await ctx.send('Patreon links are not enabled on this guild.')
|
||||||
|
|
||||||
|
@commands.command(aliases=['patreon_message'])
|
||||||
|
async def set_patreon_message(self, ctx, message):
|
||||||
|
if checks.is_admin(self.bot, ctx):
|
||||||
|
patreon_message = self.bot.con.one('select patreon_message from guild_config where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id})
|
||||||
|
if message == patreon_message:
|
||||||
|
await ctx.send('That is already the current message for this guild.')
|
||||||
|
else:
|
||||||
|
self.bot.con.run('update guild_config set patreon_message = %(message)s where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id, 'message': message})
|
||||||
|
await ctx.send(f'The patreon message for this guild has been set to:\n{message}')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'You are not authorized to run this command.')
|
||||||
|
|
||||||
|
@commands.command(aliases=['add_patreon', 'set_patreon'])
|
||||||
|
async def add_patreon_info(self, ctx, name, url):
|
||||||
|
if checks.is_admin(self.bot, ctx):
|
||||||
|
patreon_info = self.bot.con.one('select patreon_links from guild_config where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id})
|
||||||
|
patreon_links = {}
|
||||||
|
update = 0
|
||||||
|
if patreon_info:
|
||||||
|
patreon_links = json.loads(patreon_info)
|
||||||
|
if name in patreon_links:
|
||||||
|
update = 1
|
||||||
|
patreon_links[name] = url
|
||||||
|
self.bot.con.run('update guild_config set patreon_links = %(links)s where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id, 'links': json.dumps(patreon_links)})
|
||||||
|
await ctx.send(f"The Patreon link for {name} has been "
|
||||||
|
f"{'updated to the new url.' if update else'added to the config for this guild.'}")
|
||||||
|
else:
|
||||||
|
await ctx.send(f'You are not authorized to run this command.')
|
||||||
|
|
||||||
|
@commands.command(aliases=['remove_patreon'])
|
||||||
|
async def remove_patreon_info(self, ctx, name):
|
||||||
|
if checks.is_admin(self.bot, ctx):
|
||||||
|
patreon_info = self.bot.con.one('select patreon_links from guild_config where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id})
|
||||||
|
if patreon_info:
|
||||||
|
patreon_links = json.loads(patreon_info)
|
||||||
|
if name in patreon_links:
|
||||||
|
del patreon_links[name]
|
||||||
|
self.bot.con.run('update guild_config set patreon_links = %(links)s where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id, 'links': json.dumps(patreon_links)})
|
||||||
|
await ctx.send(f'The Patreon link for {name} has been removed from the config for this guild.')
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
await ctx.send(f'{name} is not in the Patreon config for this guild.')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'There is no Patreon config for this guild.')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'You are not authorized to run this command.')
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
async def enable_patreon(self, ctx, state: bool=True):
|
||||||
|
if checks.is_admin(self.bot, ctx):
|
||||||
|
patreon_status = self.bot.con.one('select patreon_enabled from guild_config where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id})
|
||||||
|
if patreon_status and state:
|
||||||
|
await ctx.send('Patreon is already enabled for this guild.')
|
||||||
|
elif patreon_status and not state:
|
||||||
|
self.bot.con.run('update guild_config set patreon_enabled = %(state)s where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id, 'state': state})
|
||||||
|
await ctx.send('Patreon has been disabled for this guild.')
|
||||||
|
elif not patreon_status and state:
|
||||||
|
self.bot.con.run('update guild_config set patreon_enabled = %(state)s where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id, 'state': state})
|
||||||
|
await ctx.send('Patreon has been enabled for this guild.')
|
||||||
|
elif not patreon_status and not state:
|
||||||
|
await ctx.send('Patreon is already disabled for this guild.')
|
||||||
|
|
||||||
|
@commands.command(aliases=['referral', 'ref'])
|
||||||
|
@commands.cooldown(1, 5, type=commands.BucketType.user)
|
||||||
|
async def referral_links(self, ctx, target: discord.Member=None):
|
||||||
|
"""Prints G-Portal Referral Links."""
|
||||||
|
if self.bot.con.one('select referral_enabled from guild_config where guild_id = %(id)s', {'id': ctx.guild.id}):
|
||||||
|
referral_info = self.bot.con.one('select referral_message,referral_links from guild_config\
|
||||||
|
where guild_id = %(id)s', {'id': ctx.guild.id})
|
||||||
|
message = referral_info[0]
|
||||||
|
referral_links = json.loads(referral_info[1])
|
||||||
|
for key in referral_links:
|
||||||
|
message = message + '\n{0}: {1}'.format(key, referral_links[key])
|
||||||
|
if target is None:
|
||||||
|
await ctx.send(message)
|
||||||
|
else:
|
||||||
|
await ctx.send('{0}\n{1}'.format(target.mention, message))
|
||||||
|
else:
|
||||||
|
await ctx.send('Referrals are not enabled on this guild.')
|
||||||
|
|
||||||
|
|
||||||
|
def setup(bot):
|
||||||
|
bot.add_cog(Patreon(bot))
|
||||||
+530
@@ -0,0 +1,530 @@
|
|||||||
|
import discord
|
||||||
|
from discord.ext import commands
|
||||||
|
import json
|
||||||
|
from srcds import rcon as rcon_con
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
import asyncio
|
||||||
|
import traceback
|
||||||
|
from .imports import checks
|
||||||
|
|
||||||
|
config_dir = 'config'
|
||||||
|
admin_id_file = 'admin_ids'
|
||||||
|
extension_dir = 'extensions'
|
||||||
|
owner_id = 351794468870946827
|
||||||
|
guild_config_file = 'guild_config'
|
||||||
|
rcon_config_file = 'server_rcon_config'
|
||||||
|
guild_config_dir = 'guild_config/'
|
||||||
|
|
||||||
|
|
||||||
|
rcon_log = logging.getLogger('rcon')
|
||||||
|
|
||||||
|
game_commands = ['admin', ]
|
||||||
|
game_prefix = '$'
|
||||||
|
|
||||||
|
|
||||||
|
class Rcon:
|
||||||
|
|
||||||
|
def __init__(self, bot):
|
||||||
|
self.bot = bot
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _listplayers(con_info):
|
||||||
|
con = rcon_con.RconConnection(con_info['ip'], con_info['port'], con_info['password'])
|
||||||
|
asyncio.sleep(5)
|
||||||
|
response = con.exec_command('listplayers')
|
||||||
|
rcon_log.info(response)
|
||||||
|
while response == b'Keep Alive\x00\x00':
|
||||||
|
asyncio.sleep(5)
|
||||||
|
response = con.exec_command('listplayers')
|
||||||
|
rcon_log.info(response)
|
||||||
|
return response.strip(b'\n \x00\x00').decode('ascii').strip()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _saveworld(con_info):
|
||||||
|
con = rcon_con.RconConnection(con_info['ip'], con_info['port'], con_info['password'])
|
||||||
|
asyncio.sleep(5)
|
||||||
|
response = con.exec_command('saveworld')
|
||||||
|
rcon_log.info(response)
|
||||||
|
while response == b'Keep Alive\x00\x00':
|
||||||
|
asyncio.sleep(5)
|
||||||
|
response = con.exec_command('saveworld')
|
||||||
|
rcon_log.info(response)
|
||||||
|
return response.strip(b'\n \x00\x00').decode('ascii').strip()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _whitelist(con_info, steam_ids):
|
||||||
|
messages = []
|
||||||
|
con = rcon_con.RconConnection(con_info['ip'], con_info['port'], con_info['password'])
|
||||||
|
asyncio.sleep(5)
|
||||||
|
for steam_id in steam_ids:
|
||||||
|
response = con.exec_command('AllowPlayerToJoinNoCheck {0}'.format(steam_id))
|
||||||
|
rcon_log.info(response)
|
||||||
|
while response == b'Keep Alive\x00\x00':
|
||||||
|
asyncio.sleep(5)
|
||||||
|
response = con.exec_command('AllowPlayerToJoinNoCheck {0}'.format(steam_id))
|
||||||
|
rcon_log.info(response)
|
||||||
|
messages.append(response.strip(b'\n \x00\x00').decode('ascii').strip())
|
||||||
|
return messages
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _broadcast(con_info, message):
|
||||||
|
messages = []
|
||||||
|
con = rcon_con.RconConnection(con_info['ip'], con_info['port'], con_info['password'])
|
||||||
|
asyncio.sleep(5)
|
||||||
|
response = con.exec_command('broadcast {0}'.format(message))
|
||||||
|
rcon_log.info(response)
|
||||||
|
while response == b'Keep Alive\x00\x00':
|
||||||
|
asyncio.sleep(5)
|
||||||
|
response = con.exec_command('broadcast {0}'.format(message))
|
||||||
|
rcon_log.info(response)
|
||||||
|
messages.append(response.strip(b'\n \x00\x00').decode('ascii').strip())
|
||||||
|
return messages
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_current_chat(con):
|
||||||
|
response = con.exec_command('getchat')
|
||||||
|
rcon_log.debug(response)
|
||||||
|
return response.strip(b'\n \x00\x00').decode('ascii').strip()
|
||||||
|
|
||||||
|
def server_chat_background_process(self, guild_id, con):
|
||||||
|
return_messages = []
|
||||||
|
try:
|
||||||
|
message = 'Server received, But no response!!'
|
||||||
|
time_now = datetime.now().timestamp()
|
||||||
|
while 'Server received, But no response!!' in message and time_now + 20 > datetime.now().timestamp():
|
||||||
|
message = self._get_current_chat(con)
|
||||||
|
rcon_log.debug(message)
|
||||||
|
time.sleep(1)
|
||||||
|
if 'Server received, But no response!!' not in message:
|
||||||
|
for msg in message.split('\n'):
|
||||||
|
msg = '{0} ||| {1}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), msg.strip())
|
||||||
|
return_messages.append(msg)
|
||||||
|
except rcon_con.RconError:
|
||||||
|
rcon_log.error('RCON Error {0}\n{1}'.format(guild_id, traceback.format_exc()))
|
||||||
|
return_messages.append('RCON Error')
|
||||||
|
except Exception as e:
|
||||||
|
rcon_log.error('Exception {0}\n{1}'.format(guild_id, traceback.format_exc()))
|
||||||
|
return_messages.append('Exception')
|
||||||
|
return_messages.append(e)
|
||||||
|
rcon_log.debug(return_messages)
|
||||||
|
return return_messages
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def admin(ctx, msg, rcon_server, admin_roles):
|
||||||
|
player = msg.split(' ||| ')[1].split(' (')[0]
|
||||||
|
con = rcon_con.RconConnection(rcon_server['ip'],
|
||||||
|
rcon_server['port'],
|
||||||
|
rcon_server['password'],
|
||||||
|
True)
|
||||||
|
con.exec_command('ServerChatToPlayer "{0}" GeeksBot: Admin Geeks have been notified you need assistance. '
|
||||||
|
'Please be patient.'.format(player))
|
||||||
|
con._sock.close()
|
||||||
|
for role in admin_roles:
|
||||||
|
msg = '{0} {1}'.format(msg, discord.utils.get(ctx.guild.roles, id=admin_roles[role]).mention)
|
||||||
|
return msg
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
@commands.guild_only()
|
||||||
|
async def monitor_chat(self, ctx, *, server=None):
|
||||||
|
"""Begins monitoring the specified ARK server for chat messages and other events.
|
||||||
|
The specified server must already be in the current guild\'s configuration.
|
||||||
|
To add and remove ARK servers from the guild see add_rcon_server and remove_rcon_server.
|
||||||
|
The server argument is not case sensitive and if the server name has two
|
||||||
|
words it can be in one of the following forms:
|
||||||
|
first last
|
||||||
|
first_last
|
||||||
|
"first last"
|
||||||
|
To view all the valid ARK servers for this guild see list_ark_servers."""
|
||||||
|
if checks.is_rcon_admin(self.bot, ctx):
|
||||||
|
if server is not None:
|
||||||
|
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||||
|
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||||
|
server = server.replace('_', ' ').title()
|
||||||
|
if server in rcon_connections:
|
||||||
|
rcon_connections[server]["monitoring_chat"] = 1
|
||||||
|
self.bot.con.run('update guild_config set rcon_connections = %(json)s where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id, 'json': json.dumps(rcon_connections)})
|
||||||
|
channel = self.bot.get_channel(rcon_connections[server]['game_chat_chan_id'])
|
||||||
|
await channel.send('Started monitoring on the {0} server.'.format(server))
|
||||||
|
await ctx.message.add_reaction('✅')
|
||||||
|
rcon_log.debug('Started monitoring on the {0} server.'.format(server))
|
||||||
|
while rcon_connections[server]["monitoring_chat"] == 1:
|
||||||
|
try:
|
||||||
|
con = rcon_con.RconConnection(rcon_connections[server]['ip'],
|
||||||
|
rcon_connections[server]['port'],
|
||||||
|
rcon_connections[server]['password'],
|
||||||
|
True)
|
||||||
|
messages = await self.bot.loop.run_in_executor(None, self.server_chat_background_process,
|
||||||
|
ctx.guild.id, con)
|
||||||
|
con._sock.close()
|
||||||
|
except TimeoutError:
|
||||||
|
rcon_log.error(traceback.format_exc())
|
||||||
|
await channel.send('TimeoutError')
|
||||||
|
await asyncio.sleep(30)
|
||||||
|
else:
|
||||||
|
rcon_log.debug('Got chat from {0}.'.format(server))
|
||||||
|
for message in messages:
|
||||||
|
rcon_log.info(message)
|
||||||
|
message = '```{0}```'.format(message)
|
||||||
|
for command in game_commands:
|
||||||
|
prefix_command = '{0}{1}'.format(game_prefix, command)
|
||||||
|
if prefix_command in message:
|
||||||
|
try:
|
||||||
|
func = getattr(self, command)
|
||||||
|
except AttributeError:
|
||||||
|
rcon_log.warning('Function not found "{0}"'.format(command))
|
||||||
|
else:
|
||||||
|
rcon_log.info(f'Sending to {command}')
|
||||||
|
message = func(ctx, message, rcon_connections['server'])
|
||||||
|
await channel.send('{0}'.format(message))
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||||
|
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||||
|
await channel.send('Monitoring Stopped')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'Server not found: {server}')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'You must include a server in this command.')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'You are not authorized to run this command.')
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
@commands.guild_only()
|
||||||
|
async def end_monitor_chat(self, ctx, *, server=None):
|
||||||
|
"""Ends chat monitoring on the specified server.
|
||||||
|
Context is the same as monitor_chat"""
|
||||||
|
if checks.is_rcon_admin(self.bot, ctx):
|
||||||
|
if server is not None:
|
||||||
|
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||||
|
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||||
|
server = server.replace('_', ' ').title()
|
||||||
|
if server in rcon_connections:
|
||||||
|
rcon_connections[server]["monitoring_chat"] = 0
|
||||||
|
self.bot.con.run('update guild_config set rcon_connections = %(json)s where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id, 'json': json.dumps(rcon_connections)})
|
||||||
|
else:
|
||||||
|
await ctx.send(f'Server not found: {server}')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'You must include a server in this command.')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'You are not authorized to run this command.')
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
@commands.guild_only()
|
||||||
|
async def listplayers(self, ctx, *, server=None):
|
||||||
|
"""Lists the players currently connected to the specified ARK server.
|
||||||
|
The specified server must already be in the current guild\'s configuration.
|
||||||
|
To add and remove ARK servers from the guild see add_rcon_server and remove_rcon_server.
|
||||||
|
The server argument is not case sensitive and if the server name has two
|
||||||
|
words it can be in one of the following forms:
|
||||||
|
first last
|
||||||
|
first_last
|
||||||
|
"first last"
|
||||||
|
To view all the valid ARK servers for this guild see list_ark_servers."""
|
||||||
|
if checks.is_rcon_admin(self.bot, ctx):
|
||||||
|
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||||
|
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||||
|
if server is not None:
|
||||||
|
server = server.replace('_', ' ').title()
|
||||||
|
if server in rcon_connections:
|
||||||
|
connection_info = rcon_connections[server]
|
||||||
|
msg = await ctx.send('Getting Data for the {0} server'.format(server.title()))
|
||||||
|
await ctx.channel.trigger_typing()
|
||||||
|
message = self._listplayers(connection_info)
|
||||||
|
await ctx.channel.trigger_typing()
|
||||||
|
await msg.delete()
|
||||||
|
await ctx.send('Players currently on the {0} server:\n{1}'.format(server.title(), message))
|
||||||
|
else:
|
||||||
|
await ctx.send('That server is not in my configuration.\nPlease add it via !add_rcon_server "{0}" '
|
||||||
|
'"ip" port "password" if you would like to get info from it.'.format(server))
|
||||||
|
else:
|
||||||
|
for server in rcon_connections:
|
||||||
|
try:
|
||||||
|
connection_info = rcon_connections[server]
|
||||||
|
msg = await ctx.send('Getting Data for the {0} server'.format(server.title()))
|
||||||
|
async with ctx.channel.typing():
|
||||||
|
message = self._listplayers(connection_info)
|
||||||
|
except Exception as e:
|
||||||
|
await msg.delete()
|
||||||
|
await ctx.send(f'Player listing failed on the {server.title()}\n{e}')
|
||||||
|
else:
|
||||||
|
await msg.delete()
|
||||||
|
await ctx.send('Players currently on the {0} server:\n{1}'.format(server.title(), message))
|
||||||
|
else:
|
||||||
|
await ctx.send(f'You are not authorized to run this command.')
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
@commands.guild_only()
|
||||||
|
async def add_rcon_server(self, ctx, server, ip, port, password):
|
||||||
|
"""Adds the specified server to the current guild\'s rcon config.
|
||||||
|
All strings (<server>, <ip>, <password>) must be contained inside double quotes."""
|
||||||
|
if checks.is_rcon_admin(self.bot, ctx):
|
||||||
|
server = server.title()
|
||||||
|
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||||
|
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||||
|
if server not in rcon_connections:
|
||||||
|
rcon_connections[server] = {
|
||||||
|
'ip': ip,
|
||||||
|
'port': port,
|
||||||
|
'password': password,
|
||||||
|
'name': server.lower().replace(' ', '_'),
|
||||||
|
'game_chat_chan_id': 0,
|
||||||
|
'msg_chan_id': 0,
|
||||||
|
'monitoring_chat': 0
|
||||||
|
}
|
||||||
|
self.bot.con.run('update guild_config set rcon_connections = %(connections)s where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id, 'connections': json.dumps(rcon_connections)})
|
||||||
|
await ctx.send('{0} server has been added to my configuration.'.format(server))
|
||||||
|
else:
|
||||||
|
await ctx.send('This server name is already in my configuration. Please choose another.')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'You are not authorized to run this command.')
|
||||||
|
await ctx.message.delete()
|
||||||
|
await ctx.send('Command deleted to prevent password leak.')
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
@commands.guild_only()
|
||||||
|
async def remove_rcon_server(self, ctx, server):
|
||||||
|
"""removes the specified server from the current guild\'s rcon config.
|
||||||
|
All strings <server> must be contained inside double quotes."""
|
||||||
|
if checks.is_rcon_admin(self.bot, ctx):
|
||||||
|
server = server.title()
|
||||||
|
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||||
|
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||||
|
if server in rcon_connections:
|
||||||
|
del rcon_connections[server]
|
||||||
|
self.bot.con.run('update guild_config set rcon_connections = %(connections)s where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id, 'connections': json.dumps(rcon_connections)})
|
||||||
|
await ctx.send('{0} has been removed from my configuration.'.format(server))
|
||||||
|
else:
|
||||||
|
await ctx.send('{0} is not in my configuration.'.format(server))
|
||||||
|
else:
|
||||||
|
await ctx.send(f'You are not authorized to run this command.')
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
@commands.guild_only()
|
||||||
|
async def add_whitelist(self, ctx, *, steam_ids=None):
|
||||||
|
"""Adds the included Steam 64 IDs to the running whitelist on all the ARK servers in the current guild\'s rcon config.
|
||||||
|
Steam 64 IDs should be a comma seperated list of IDs.
|
||||||
|
Example: 76561198024193239,76561198024193239,76561198024193239"""
|
||||||
|
if checks.is_rcon_admin(self.bot, ctx):
|
||||||
|
if steam_ids is not None:
|
||||||
|
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||||
|
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||||
|
error = 0
|
||||||
|
error_msg = ''
|
||||||
|
success_msg = 'Adding to the running whitelist on all servers.'
|
||||||
|
steam_ids = steam_ids.replace(', ', ',').replace(' ', ',').split(',')
|
||||||
|
for (i, steam_id) in enumerate(steam_ids):
|
||||||
|
try:
|
||||||
|
steam_id = int(steam_id)
|
||||||
|
except ValueError:
|
||||||
|
error = 1
|
||||||
|
error_msg = '{0}\n__**ERROR:**__ {1} is not a valid Steam64 ID'.format(error_msg, steam_id)
|
||||||
|
else:
|
||||||
|
steam_ids[i] = steam_id
|
||||||
|
if error == 0:
|
||||||
|
msg = await ctx.send(success_msg)
|
||||||
|
for server in rcon_connections:
|
||||||
|
try:
|
||||||
|
success_msg = '{0}\n\n{1}:'.format(success_msg, server.title())
|
||||||
|
await msg.edit(content=success_msg.strip())
|
||||||
|
messages = await self.bot.loop.run_in_executor(None,
|
||||||
|
self._whitelist,
|
||||||
|
rcon_connections[server],
|
||||||
|
steam_ids)
|
||||||
|
except Exception as e:
|
||||||
|
success_msg = '{0}\n{1}'.format(success_msg, e)
|
||||||
|
await msg.edit(content=success_msg.strip())
|
||||||
|
else:
|
||||||
|
for message in messages:
|
||||||
|
success_msg = '{0}\n{1}'.format(success_msg, message.strip())
|
||||||
|
await msg.edit(content=success_msg.strip())
|
||||||
|
await msg.add_reaction('✅')
|
||||||
|
else:
|
||||||
|
await ctx.send(error_msg)
|
||||||
|
else:
|
||||||
|
await ctx.send('I need a list of steam IDs to add to the whitelist.')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'You are not authorized to run this command.')
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
@commands.guild_only()
|
||||||
|
async def saveworld(self, ctx, *, server=None):
|
||||||
|
"""Runs SaveWorld on the specified ARK server.
|
||||||
|
If a server is not specified it will default to running saveworld on all servers in the guild\'s config.
|
||||||
|
Will print out "World Saved" for each server when the command completes successfully."""
|
||||||
|
if checks.is_rcon_admin(self.bot, ctx):
|
||||||
|
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||||
|
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||||
|
success_msg = 'Running saveworld'
|
||||||
|
if server is None:
|
||||||
|
success_msg += ' on all the servers:'
|
||||||
|
msg = await ctx.send(success_msg)
|
||||||
|
for server in rcon_connections:
|
||||||
|
try:
|
||||||
|
success_msg = '{0}\n\n{1}:'.format(success_msg, server.title())
|
||||||
|
await msg.edit(content=success_msg.strip())
|
||||||
|
message = await self.bot.loop.run_in_executor(None, self._saveworld, rcon_connections[server])
|
||||||
|
except Exception as e:
|
||||||
|
success_msg = '{0}\n{1}'.format(success_msg, e.strip())
|
||||||
|
await msg.edit(content=success_msg.strip())
|
||||||
|
else:
|
||||||
|
success_msg = '{0}\n{1}'.format(success_msg, message.strip())
|
||||||
|
await msg.edit(content=success_msg.strip())
|
||||||
|
await msg.add_reaction('✅')
|
||||||
|
elif server.replace('_', ' ').title() in rcon_connections:
|
||||||
|
success_msg = '{0} {1}:'.format(success_msg, server.replace('_', ' ').title())
|
||||||
|
msg = await ctx.send(success_msg)
|
||||||
|
message = await self.bot.loop.run_in_executor(None, self._saveworld, rcon_connections[server.title()])
|
||||||
|
success_msg = '{0}\n{1}'.format(success_msg, message.strip())
|
||||||
|
await msg.edit(content=success_msg.strip())
|
||||||
|
await msg.add_reaction('✅')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'{server.title()} is not currently in the configuration for this guild.')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'You are not authorized to run this command.')
|
||||||
|
|
||||||
|
@commands.group(case_insensitive=True)
|
||||||
|
async def broadcast(self, ctx):
|
||||||
|
"""Run help broadcast for more info"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@broadcast.command(name='all')
|
||||||
|
@commands.guild_only()
|
||||||
|
async def broadcast_all(self, ctx, *, message=None):
|
||||||
|
"""Sends a broadcast message to all servers in the guild config.
|
||||||
|
The message will be prefixed with the Discord name of the person running the command.
|
||||||
|
Will print "Success" for each server once the broadcast is sent."""
|
||||||
|
if checks.is_rcon_admin(self.bot, ctx):
|
||||||
|
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||||
|
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||||
|
if message is not None:
|
||||||
|
message = f'{ctx.author.display_name}: {message}'
|
||||||
|
success_msg = f'Broadcasting "{message}" to all servers.'
|
||||||
|
msg = await ctx.send(success_msg)
|
||||||
|
for server in rcon_connections:
|
||||||
|
try:
|
||||||
|
success_msg = '{0}\n\n{1}:'.format(success_msg, server.title())
|
||||||
|
await msg.edit(content=success_msg.strip())
|
||||||
|
messages = await self.bot.loop.run_in_executor(None,
|
||||||
|
self._broadcast,
|
||||||
|
rcon_connections[server],
|
||||||
|
message)
|
||||||
|
except Exception as e:
|
||||||
|
success_msg = '{0}\n{1}'.format(success_msg, e.strip())
|
||||||
|
await msg.edit(content=success_msg.strip())
|
||||||
|
else:
|
||||||
|
for mesg in messages:
|
||||||
|
if mesg == 'Server received, But no response!!':
|
||||||
|
mesg = 'Success'
|
||||||
|
success_msg = '{0}\n{1}'.format(success_msg, mesg.strip())
|
||||||
|
await msg.edit(content=success_msg.strip())
|
||||||
|
await msg.add_reaction('✅')
|
||||||
|
else:
|
||||||
|
await ctx.send('You must include a message with this command.')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'You are not authorized to run this command.')
|
||||||
|
|
||||||
|
@broadcast.command(name='server')
|
||||||
|
@commands.guild_only()
|
||||||
|
async def broadcast_server(self, ctx, server, *, message=None):
|
||||||
|
"""Sends a broadcast message to the specified server that is in the guild's config.
|
||||||
|
The message will be prefixed with the Discord name of the person running the command.
|
||||||
|
If <server> has more than one word in it's name it will either need to be surrounded
|
||||||
|
by double quotes or the words separated by _"""
|
||||||
|
if checks.is_rcon_admin(self.bot, ctx):
|
||||||
|
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||||
|
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||||
|
if server is not None:
|
||||||
|
server = server.replace('_', ' ').title()
|
||||||
|
if message is not None:
|
||||||
|
message = f'{ctx.author.display_name}: {message}'
|
||||||
|
success_msg = f'Broadcasting "{message}" to {server}.'
|
||||||
|
msg = await ctx.send(success_msg)
|
||||||
|
if server in rcon_connections:
|
||||||
|
messages = await self.bot.loop.run_in_executor(None,
|
||||||
|
self._broadcast,
|
||||||
|
rcon_connections[server],
|
||||||
|
message)
|
||||||
|
for mesg in messages:
|
||||||
|
if mesg != 'Server received, But no response!!':
|
||||||
|
success_msg = '{0}\n{1}'.format(success_msg, mesg.strip())
|
||||||
|
await msg.edit(content=success_msg.strip())
|
||||||
|
await msg.add_reaction('✅')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'{server} is not in the config for this guild')
|
||||||
|
else:
|
||||||
|
await ctx.send('You must include a message with this command.')
|
||||||
|
else:
|
||||||
|
await ctx.send('You must include a server with this command')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'You are not authorized to run this command.')
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
@commands.guild_only()
|
||||||
|
async def create_server_chat_chans(self, ctx):
|
||||||
|
"""Creates a category and server chat channels in the current guild.
|
||||||
|
The category will be named "Server Chats" and read_messages is disabled for the guild default role (everyone)
|
||||||
|
This can be overridden by modifying the category's permissions.
|
||||||
|
Inside this category a channel will be created for each server in the current guild's rcon config
|
||||||
|
and these channel's permissions will be synced to the category.
|
||||||
|
These channels will be added to the guild's rcon config and are where the
|
||||||
|
server chat messages will be sent when monitor_chat is run."""
|
||||||
|
if checks.is_rcon_admin(self.bot, ctx):
|
||||||
|
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||||
|
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||||
|
edited = 0
|
||||||
|
category = discord.utils.get(ctx.guild.categories, name='Server Chats')
|
||||||
|
if category is None:
|
||||||
|
overrides = {ctx.guild.default_role: discord.PermissionOverwrite(read_messages=False)}
|
||||||
|
category = await ctx.guild.create_category('Server Chats', overwrites=overrides)
|
||||||
|
channels = ctx.guild.channels
|
||||||
|
cat_chans = []
|
||||||
|
for channel in channels:
|
||||||
|
if channel.category_id == category.id:
|
||||||
|
cat_chans.append(channel)
|
||||||
|
for server in rcon_connections:
|
||||||
|
exists = 0
|
||||||
|
if cat_chans:
|
||||||
|
for channel in cat_chans:
|
||||||
|
if rcon_connections[server]['game_chat_chan_id'] == channel.id:
|
||||||
|
exists = 1
|
||||||
|
if exists == 0:
|
||||||
|
print('Creating {}'.format(server))
|
||||||
|
chan = await ctx.guild.create_text_channel(rcon_connections[server]['name'], category=category)
|
||||||
|
rcon_connections[server]['game_chat_chan_id'] = chan.id
|
||||||
|
edited = 1
|
||||||
|
if edited == 1:
|
||||||
|
self.bot.con.run('update guild_config set rcon_connections = %(json)s where guild_id = %(id)s',
|
||||||
|
{'id': ctx.guild.id, 'json': json.dumps(rcon_connections)})
|
||||||
|
await ctx.message.add_reaction('✅')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'You are not authorized to run this command.')
|
||||||
|
|
||||||
|
@commands.command(aliases=['servers', 'list_servers'])
|
||||||
|
@commands.guild_only()
|
||||||
|
@commands.check(checks.is_restricted_chan)
|
||||||
|
async def list_ark_servers(self, ctx):
|
||||||
|
"""Returns a list of all the ARK servers in the current guild\'s config."""
|
||||||
|
servers = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||||
|
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||||
|
em = discord.Embed(style='rich',
|
||||||
|
title=f'__**There are currently {len(servers)} ARK servers in my config:**__',
|
||||||
|
color=discord.Colour.green()
|
||||||
|
)
|
||||||
|
if ctx.guild.icon:
|
||||||
|
em.set_thumbnail(url=f'{ctx.guild.icon_url}')
|
||||||
|
for server in servers:
|
||||||
|
description = f"""
|
||||||
|
឵ **IP:** {servers[server]['ip']}:{servers[server]['port']}
|
||||||
|
឵ **Steam Connect:** [steam://connect/{servers[server]['ip']}:{servers[server]['port']}]\
|
||||||
|
(steam://connect/{servers[server]['ip']}:{servers[server]['port']})"""
|
||||||
|
em.add_field(name=f'__***{server}***__', value=description, inline=False)
|
||||||
|
await ctx.send(embed=em)
|
||||||
|
|
||||||
|
|
||||||
|
def setup(bot):
|
||||||
|
bot.add_cog(Rcon(bot))
|
||||||
+176
@@ -0,0 +1,176 @@
|
|||||||
|
|
||||||
|
from discord.ext import commands
|
||||||
|
import asyncio
|
||||||
|
import traceback
|
||||||
|
import discord
|
||||||
|
import inspect
|
||||||
|
import textwrap
|
||||||
|
from contextlib import redirect_stdout
|
||||||
|
import io
|
||||||
|
from .imports.utils import paginate, run_command
|
||||||
|
|
||||||
|
ownerids = [351794468870946827, 275280442884751360]
|
||||||
|
ownerid = 351794468870946827
|
||||||
|
|
||||||
|
|
||||||
|
class Repl:
|
||||||
|
|
||||||
|
def __init__(self, bot):
|
||||||
|
self.bot = bot
|
||||||
|
self._last_result = None
|
||||||
|
self.sessions = set()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def cleanup_code(content):
|
||||||
|
"""Automatically removes code blocks from the code."""
|
||||||
|
if content.startswith('```') and content.endswith('```'):
|
||||||
|
return '\n'.join(content.split('\n')[1:(- 1)])
|
||||||
|
return content.strip('` \n')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_syntax_error(e):
|
||||||
|
if e.text is None:
|
||||||
|
return '```py\n{0.__class__.__name__}: {0}\n```'.format(e)
|
||||||
|
return '```py\n{0.text}{1:>{0.offset}}\n{2}: {0}```'.format(e, '^', type(e).__name__)
|
||||||
|
|
||||||
|
@commands.command(hidden=True, name='exec')
|
||||||
|
async def _eval(self, ctx, *, body: str):
|
||||||
|
if ctx.author.id != ownerid:
|
||||||
|
return
|
||||||
|
env = {
|
||||||
|
'bot': self.bot,
|
||||||
|
'ctx': ctx,
|
||||||
|
'channel': ctx.channel,
|
||||||
|
'author': ctx.author,
|
||||||
|
'server': ctx.guild,
|
||||||
|
'message': ctx.message,
|
||||||
|
'_': self._last_result,
|
||||||
|
}
|
||||||
|
env.update(globals())
|
||||||
|
body = self.cleanup_code(body)
|
||||||
|
stdout = io.StringIO()
|
||||||
|
to_compile = 'async def func():\n%s' % textwrap.indent(body, ' ')
|
||||||
|
try:
|
||||||
|
exec(to_compile, env)
|
||||||
|
except SyntaxError as e:
|
||||||
|
return await ctx.send(self.get_syntax_error(e))
|
||||||
|
func = env['func']
|
||||||
|
# noinspection PyBroadException
|
||||||
|
try:
|
||||||
|
with redirect_stdout(stdout):
|
||||||
|
ret = await func()
|
||||||
|
except Exception:
|
||||||
|
value = stdout.getvalue()
|
||||||
|
await ctx.send('```py\n{}{}\n```'.format(value, traceback.format_exc()))
|
||||||
|
else:
|
||||||
|
value = stdout.getvalue()
|
||||||
|
# noinspection PyBroadException
|
||||||
|
try:
|
||||||
|
await ctx.message.add_reaction('✅')
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if ret is None:
|
||||||
|
if value:
|
||||||
|
for page in paginate(value):
|
||||||
|
await ctx.send(page)
|
||||||
|
else:
|
||||||
|
self._last_result = ret
|
||||||
|
if value:
|
||||||
|
for page in paginate(value):
|
||||||
|
await ctx.send(page)
|
||||||
|
for page in paginate(ret):
|
||||||
|
await ctx.send(page)
|
||||||
|
|
||||||
|
@commands.command(hidden=True)
|
||||||
|
async def repl(self, ctx):
|
||||||
|
if ctx.author.id != ownerid:
|
||||||
|
return
|
||||||
|
msg = ctx.message
|
||||||
|
variables = {
|
||||||
|
'ctx': ctx,
|
||||||
|
'bot': self.bot,
|
||||||
|
'message': msg,
|
||||||
|
'server': msg.guild,
|
||||||
|
'channel': msg.channel,
|
||||||
|
'author': msg.author,
|
||||||
|
'_': None,
|
||||||
|
}
|
||||||
|
if msg.channel.id in self.sessions:
|
||||||
|
await ctx.send('Already running a REPL session in this channel. Exit it with `quit`.')
|
||||||
|
return
|
||||||
|
self.sessions.add(msg.channel.id)
|
||||||
|
await ctx.send('Enter code to execute or evaluate. `exit()` or `quit` to exit.')
|
||||||
|
while True:
|
||||||
|
response = await self.bot.wait_for('message', check=(lambda m: m.content.startswith('`')))
|
||||||
|
if response.author.id == ownerid:
|
||||||
|
cleaned = self.cleanup_code(response.content)
|
||||||
|
if cleaned in ('quit', 'exit', 'exit()'):
|
||||||
|
await response.channel.send('Exiting.')
|
||||||
|
self.sessions.remove(msg.channel.id)
|
||||||
|
return
|
||||||
|
executor = exec
|
||||||
|
if cleaned.count('\n') == 0:
|
||||||
|
try:
|
||||||
|
code = compile(cleaned, '<repl session>', 'eval')
|
||||||
|
except SyntaxError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
executor = eval
|
||||||
|
if executor is exec:
|
||||||
|
try:
|
||||||
|
code = compile(cleaned, '<repl session>', 'exec')
|
||||||
|
except SyntaxError as e:
|
||||||
|
await response.channel.send(self.get_syntax_error(e))
|
||||||
|
continue
|
||||||
|
variables['message'] = response
|
||||||
|
fmt = None
|
||||||
|
stdout = io.StringIO()
|
||||||
|
# noinspection PyBroadException
|
||||||
|
try:
|
||||||
|
with redirect_stdout(stdout):
|
||||||
|
result = executor(code, variables)
|
||||||
|
if inspect.isawaitable(result):
|
||||||
|
result = await result
|
||||||
|
except Exception:
|
||||||
|
value = stdout.getvalue()
|
||||||
|
fmt = '{}{}'.format(value, traceback.format_exc())
|
||||||
|
else:
|
||||||
|
value = stdout.getvalue()
|
||||||
|
if result is not None:
|
||||||
|
fmt = '{}{}'.format(value, result)
|
||||||
|
variables['_'] = result
|
||||||
|
elif value:
|
||||||
|
fmt = '{}'.format(value)
|
||||||
|
try:
|
||||||
|
if fmt is not None:
|
||||||
|
if len(fmt) > 1990:
|
||||||
|
for page in paginate(fmt):
|
||||||
|
await response.channel.send(page)
|
||||||
|
await ctx.send(response.channel)
|
||||||
|
else:
|
||||||
|
await response.channel.send(f'```py\n{fmt}\n```')
|
||||||
|
except discord.Forbidden:
|
||||||
|
pass
|
||||||
|
except discord.HTTPException as e:
|
||||||
|
await msg.channel.send('Unexpected error: `{}`'.format(e))
|
||||||
|
|
||||||
|
@commands.command(hidden=True)
|
||||||
|
async def os(self, ctx, *, body: str):
|
||||||
|
if ctx.author.id != ownerid:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
body = self.cleanup_code(body).split(' ')
|
||||||
|
result = await asyncio.wait_for(self.bot.loop.create_task(run_command(body)), 10)
|
||||||
|
value = result
|
||||||
|
for page in paginate(value):
|
||||||
|
await ctx.send(page)
|
||||||
|
await ctx.message.add_reaction('✅')
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
value = f"Command did not complete in the time allowed."
|
||||||
|
for page in paginate(value):
|
||||||
|
await ctx.send(page)
|
||||||
|
await ctx.message.add_reaction('❌')
|
||||||
|
|
||||||
|
|
||||||
|
def setup(bot):
|
||||||
|
bot.add_cog(Repl(bot))
|
||||||
+533
@@ -0,0 +1,533 @@
|
|||||||
|
import discord
|
||||||
|
from discord.ext import commands
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import math
|
||||||
|
import psutil
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import asyncio
|
||||||
|
import async_timeout
|
||||||
|
from .imports import checks
|
||||||
|
import pytz
|
||||||
|
import gspread
|
||||||
|
from oauth2client.service_account import ServiceAccountCredentials
|
||||||
|
|
||||||
|
config_dir = 'config/'
|
||||||
|
admin_id_file = 'admin_ids'
|
||||||
|
extension_dir = 'extensions'
|
||||||
|
owner_id = 351794468870946827
|
||||||
|
embed_color = discord.Colour.from_rgb(49, 107, 111)
|
||||||
|
bot_config_file = 'bot_config.json'
|
||||||
|
invite_match = '(https?://)?(www.)?discord(app.com/(invite|oauth2)|.gg|.io)/[\w\d_\-?=&/]+'
|
||||||
|
|
||||||
|
utils_log = logging.getLogger('utils')
|
||||||
|
clock_emojis = ['🕛', '🕐', '🕑', '🕒', '🕓', '🕔', '🕕', '🕖', '🕗', '🕘', '🕙', '🕚']
|
||||||
|
|
||||||
|
|
||||||
|
class Utils:
|
||||||
|
def __init__(self, bot):
|
||||||
|
self.bot = bot
|
||||||
|
|
||||||
|
async def _4_hour_ping(self, channel, message, wait_time):
|
||||||
|
channel = self.bot.get_channel(channel)
|
||||||
|
time_now = datetime.utcnow()
|
||||||
|
await channel.send(message)
|
||||||
|
while True:
|
||||||
|
if time_now < datetime.utcnow() - timedelta(seconds=wait_time+10):
|
||||||
|
await channel.send(message)
|
||||||
|
time_now = datetime.utcnow()
|
||||||
|
await asyncio.sleep(wait_time/100)
|
||||||
|
|
||||||
|
async def background_ping(self, ctx, i):
|
||||||
|
def check(message):
|
||||||
|
return message.author == ctx.guild.me and 'ping_test' in message.content
|
||||||
|
msg = await self.bot.wait_for('message', timeout=5, check=check)
|
||||||
|
self.bot.ping_times[i]['rec'] = msg
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
async def channel_ping(self, ctx, wait_time: float=10, message: str='=bump', channel: int=265828729970753537):
|
||||||
|
await ctx.send('Starting Background Process.')
|
||||||
|
self.bot.loop.create_task(self._4_hour_ping(channel, message, wait_time))
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
@commands.is_owner()
|
||||||
|
async def sysinfo(self, ctx):
|
||||||
|
await ctx.send(f'''
|
||||||
|
```ml
|
||||||
|
CPU Percentages: {psutil.cpu_percent(percpu=True)}
|
||||||
|
Memory Usage: {psutil.virtual_memory().percent}%
|
||||||
|
Disc Usage: {psutil.disk_usage("/").percent}%
|
||||||
|
```''')
|
||||||
|
|
||||||
|
@commands.command(hidden=True)
|
||||||
|
async def role(self, ctx, role: str):
|
||||||
|
if ctx.guild.id == 396156980974059531 and role != 'Admin' and role != 'Admin Geeks':
|
||||||
|
role = discord.utils.get(ctx.guild.roles, name=role)
|
||||||
|
if role is not None:
|
||||||
|
await ctx.message.author.add_roles(role)
|
||||||
|
await ctx.send("Roles Updated")
|
||||||
|
else:
|
||||||
|
await ctx.send('Unknown Role')
|
||||||
|
else:
|
||||||
|
await ctx.send("You are not authorized to send this command.")
|
||||||
|
|
||||||
|
@commands.command(aliases=['oauth', 'link'])
|
||||||
|
@commands.cooldown(1, 5, type=commands.BucketType.user)
|
||||||
|
async def invite(self, ctx, guy: discord.User=None):
|
||||||
|
"""Shows you the bot's invite link.
|
||||||
|
If you pass in an ID of another bot, it gives you the invite link to that bot.
|
||||||
|
"""
|
||||||
|
guy = guy or self.bot.user
|
||||||
|
url = discord.utils.oauth_url(guy.id)
|
||||||
|
await ctx.send(f'**{url}**')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_date_string(time, time_now):
|
||||||
|
diff = (time_now - time)
|
||||||
|
date_str = time.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
return f"{diff.days} {'day' if diff.days == 1 else 'days'} " \
|
||||||
|
f"{diff.seconds // 3600} {'hour' if diff.seconds // 3600 == 1 else 'hours'} " \
|
||||||
|
f"{diff.seconds % 3600 // 60} {'minute' if diff.seconds % 3600 // 60 == 1 else 'minutes'} " \
|
||||||
|
f"{diff.seconds % 3600 % 60} {'second' if diff.seconds % 3600 % 60 == 1 else 'seconds'} ago.\n{date_str}"
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
@commands.cooldown(1, 5, type=commands.BucketType.user)
|
||||||
|
async def me(self, ctx):
|
||||||
|
"""Prints out your user information."""
|
||||||
|
em = discord.Embed(style='rich',
|
||||||
|
title=f'{ctx.author.name}#{ctx.author.discriminator} ({ctx.author.display_name})',
|
||||||
|
description=f'({ctx.author.id})',
|
||||||
|
color=embed_color)
|
||||||
|
em.set_thumbnail(url=f'{ctx.author.avatar_url}')
|
||||||
|
em.add_field(name=f'Highest Role:',
|
||||||
|
value=f'{ctx.author.top_role}',
|
||||||
|
inline=True)
|
||||||
|
em.add_field(name=f'Bot:',
|
||||||
|
value=f'{ctx.author.bot}',
|
||||||
|
inline=True)
|
||||||
|
em.add_field(name=f'Joined Guild:',
|
||||||
|
value=f'{self.create_date_string(ctx.author.joined_at, ctx.message.created_at)}',
|
||||||
|
inline=False)
|
||||||
|
em.add_field(name=f'Joined Discord:',
|
||||||
|
value=f'{self.create_date_string(ctx.author.created_at, ctx.message.created_at)}',
|
||||||
|
inline=False)
|
||||||
|
em.add_field(name=f'Current Status:',
|
||||||
|
value=f'{ctx.author.status}',
|
||||||
|
inline=True)
|
||||||
|
em.add_field(name=f"Currently{' '+ctx.author.activity.type.name.title() if ctx.author.activity else ''}:",
|
||||||
|
value=f"{ctx.author.activity.name if ctx.author.activity else 'Not doing anything important.'}",
|
||||||
|
inline=True)
|
||||||
|
count = 0
|
||||||
|
async for message in ctx.channel.history(after=(ctx.message.created_at - timedelta(hours=1))):
|
||||||
|
if message.author == ctx.author:
|
||||||
|
count += 1
|
||||||
|
em.add_field(name=f'Activity:',
|
||||||
|
value=f'You have sent {count} '
|
||||||
|
f'{"message" if count == 1 else "messages"} in the last hour to this channel.',
|
||||||
|
inline=False)
|
||||||
|
await ctx.send(embed=em)
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
@commands.cooldown(1, 5, type=commands.BucketType.user)
|
||||||
|
async def user(self, ctx, member: discord.Member):
|
||||||
|
"""Prints User information.
|
||||||
|
<member> should be in the form @Dusty.P#0001"""
|
||||||
|
em = discord.Embed(style='rich',
|
||||||
|
title=f'{member.name}#{member.discriminator} ({member.display_name})',
|
||||||
|
description=f'({member.id})',
|
||||||
|
color=embed_color)
|
||||||
|
em.set_thumbnail(url=f'{member.avatar_url}')
|
||||||
|
em.add_field(name=f'Highest Role:',
|
||||||
|
value=f'{member.top_role}',
|
||||||
|
inline=True)
|
||||||
|
em.add_field(name=f'Bot:',
|
||||||
|
value=f'{member.bot}',
|
||||||
|
inline=True)
|
||||||
|
em.add_field(name=f'Joined Guild:',
|
||||||
|
value=f'{self.create_date_string(member.joined_at,ctx.message.created_at)}',
|
||||||
|
inline=False)
|
||||||
|
em.add_field(name=f'Joined Discord:',
|
||||||
|
value=f'{self.create_date_string(member.created_at,ctx.message.created_at)}',
|
||||||
|
inline=False)
|
||||||
|
em.add_field(name=f'Current Status:',
|
||||||
|
value=f'{member.status}',
|
||||||
|
inline=True)
|
||||||
|
em.add_field(name=f"Currently{' '+member.activity.type.name.title() if member.activity else ''}:",
|
||||||
|
value=f"{member.activity.name if member.activity else 'Not doing anything important.'}",
|
||||||
|
inline=True)
|
||||||
|
count = 0
|
||||||
|
async for message in ctx.channel.history(after=(ctx.message.created_at - timedelta(hours=1))):
|
||||||
|
if message.author == member:
|
||||||
|
count += 1
|
||||||
|
em.add_field(name=f'Activity:',
|
||||||
|
value=f'{member.display_name} has sent {count} '
|
||||||
|
f'{"message" if count == 1 else "messages"} in the last hour to this channel.',
|
||||||
|
inline=False)
|
||||||
|
await ctx.send(embed=em)
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
@commands.cooldown(1, 5, type=commands.BucketType.user)
|
||||||
|
async def ping(self, ctx, mode='normal', count: int=2):
|
||||||
|
"""Check the Bot\'s connection to Discord"""
|
||||||
|
em = discord.Embed(style='rich',
|
||||||
|
title=f'Pong 🏓',
|
||||||
|
color=discord.Colour.green()
|
||||||
|
)
|
||||||
|
msg = await ctx.send(embed=em)
|
||||||
|
time1 = ctx.message.created_at
|
||||||
|
time = (msg.created_at - time1).total_seconds() * 1000
|
||||||
|
em.description = f'''Response Time: **{math.ceil(time)}ms**
|
||||||
|
Discord Latency: **{math.ceil(self.bot.latency*1000)}ms**'''
|
||||||
|
await msg.edit(embed=em)
|
||||||
|
|
||||||
|
if mode == 'comp':
|
||||||
|
try:
|
||||||
|
count = int(count)
|
||||||
|
except ValueError:
|
||||||
|
await ctx.send('Not a valid count. Must be a whole number.')
|
||||||
|
else:
|
||||||
|
if count > 24:
|
||||||
|
await ctx.send('24 Pings is the max allowed. Setting count to 24.', delete_after=5)
|
||||||
|
count = 24
|
||||||
|
self.bot.ping_times = []
|
||||||
|
times = []
|
||||||
|
for i in range(count):
|
||||||
|
self.bot.ping_times.append({})
|
||||||
|
self.bot.loop.create_task(self.background_ping(ctx, i))
|
||||||
|
await asyncio.sleep(0.1)
|
||||||
|
self.bot.ping_times[i]['snd'] = await ctx.send('ping_test')
|
||||||
|
now = datetime.utcnow()
|
||||||
|
while 'rec' not in self.bot.ping_times[i]:
|
||||||
|
now = datetime.utcnow()
|
||||||
|
if now.timestamp() > self.bot.ping_times[i]['snd'].created_at.timestamp()+5:
|
||||||
|
break
|
||||||
|
if 'rec' in self.bot.ping_times[i]:
|
||||||
|
time = now - self.bot.ping_times[i]['snd'].created_at
|
||||||
|
time = time.total_seconds()
|
||||||
|
times.append(time)
|
||||||
|
value = f"Message Sent:" \
|
||||||
|
f"{datetime.strftime(self.bot.ping_times[i]['snd'].created_at, '%H:%M:%S.%f')}" \
|
||||||
|
f"Response Received: {datetime.strftime(now, '%H:%M:%S.%f')}" \
|
||||||
|
f"Total Time: {math.ceil(time * 1000)}ms"
|
||||||
|
await self.bot.ping_times[i]['rec'].delete()
|
||||||
|
em.add_field(name=f'Ping Test {i}', value=value, inline=True)
|
||||||
|
else:
|
||||||
|
em.add_field(name=f'Ping Test {i}', value='Timeout...', inline=True)
|
||||||
|
total_time = 0
|
||||||
|
print(times)
|
||||||
|
for time in times:
|
||||||
|
total_time += time * 1000
|
||||||
|
em.add_field(value=f'Total Time for Comprehensive test: {math.ceil(total_time)}ms',
|
||||||
|
name=f'Average: **{round(total_time/count,1)}ms**',
|
||||||
|
inline=False)
|
||||||
|
await msg.edit(embed=em)
|
||||||
|
|
||||||
|
@commands.group(case_insensitive=True)
|
||||||
|
async def admin(self, ctx):
|
||||||
|
"""Run help admin for more info"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@admin.command(name='new', aliases=['nr'])
|
||||||
|
@commands.cooldown(1, 30, type=commands.BucketType.user)
|
||||||
|
async def new_admin_request(self, ctx, *, request_msg=None):
|
||||||
|
"""Submit a new request for admin assistance.
|
||||||
|
The admin will be notified when your request is made and it will be added to the request list for this guild.
|
||||||
|
"""
|
||||||
|
if ctx.guild:
|
||||||
|
if request_msg is not None:
|
||||||
|
if len(request_msg) < 1000:
|
||||||
|
self.bot.con.run('insert into admin_requests (issuing_member_id, guild_orig, request_text,'
|
||||||
|
'request_time) values (%(member_id)s, %(guild_id)s, %(text)s, %(time)s)',
|
||||||
|
{'member_id': ctx.author.id, 'guild_id': ctx.guild.id, 'text': request_msg,
|
||||||
|
'time': ctx.message.created_at})
|
||||||
|
channel = self.bot.con.one(f'select admin_chat from guild_config where guild_id = {ctx.guild.id}')
|
||||||
|
if channel:
|
||||||
|
chan = discord.utils.get(ctx.guild.channels, id=channel)
|
||||||
|
msg = ''
|
||||||
|
admin_roles = []
|
||||||
|
roles = self.bot.con.one(f'select admin_roles,rcon_admin_roles from guild_config where '
|
||||||
|
f'guild_id = %(id)s', {'id': ctx.guild.id})
|
||||||
|
request_id = self.bot.con.one(f'select id from admin_requests where '
|
||||||
|
f'issuing_member_id = %(member_id)s and request_time = %(time)s',
|
||||||
|
{'member_id': ctx.author.id, 'time': ctx.message.created_at})
|
||||||
|
for item in roles:
|
||||||
|
i = json.loads(item)
|
||||||
|
for j in i:
|
||||||
|
if i[j] not in admin_roles:
|
||||||
|
admin_roles.append(i[j])
|
||||||
|
for role in admin_roles:
|
||||||
|
msg = '{0} {1}'.format(msg, discord.utils.get(ctx.guild.roles, id=role).mention)
|
||||||
|
msg += f"New Request ID: {request_id} " \
|
||||||
|
f"{ctx.author.mention} has requested assistance: \n" \
|
||||||
|
f"```{request_msg}``` \n" \
|
||||||
|
f"Requested on: {datetime.strftime(ctx.message.created_at, '%Y-%m-%d at %H:%M:%S')} GMT"
|
||||||
|
await chan.send(msg)
|
||||||
|
await ctx.send('The Admin have received your request.')
|
||||||
|
else:
|
||||||
|
await ctx.send('Request is too long, please keep your message to less than 1000 characters.')
|
||||||
|
else:
|
||||||
|
await ctx.send('Please include a message containing information about your request.')
|
||||||
|
else:
|
||||||
|
await ctx.send('This command must be run from inside a guild.')
|
||||||
|
|
||||||
|
@admin.command(name='list', aliases=['lr'])
|
||||||
|
@commands.cooldown(1, 5, type=commands.BucketType.user)
|
||||||
|
async def list_admin_requests(self, ctx, assigned_to: discord.Member=None):
|
||||||
|
"""Returns a list of all active Admin help requests for this guild
|
||||||
|
|
||||||
|
If a user runs this command it will return all the requests that they have submitted and are still open.
|
||||||
|
- The [assigned_to] argument is ignored but will still give an error if an incorrect value is entered.
|
||||||
|
|
||||||
|
If an admin runs this command it will return all the open requests for this guild.
|
||||||
|
- If the [assigned_to] argument is included it will instead return all open requests that
|
||||||
|
are assigned to the specified admin.
|
||||||
|
"""
|
||||||
|
em = discord.Embed(style='rich',
|
||||||
|
title=f'Admin Help Requests',
|
||||||
|
color=discord.Colour.green()
|
||||||
|
)
|
||||||
|
if checks.is_admin(self.bot, ctx) or checks.is_rcon_admin(self.bot, ctx):
|
||||||
|
if assigned_to is None:
|
||||||
|
requests = self.bot.con.all(f'select * from admin_requests where guild_orig = %(guild_id)s '
|
||||||
|
f'and completed_time is null', {'guild_id': ctx.guild.id})
|
||||||
|
em.title = f'Admin help requests for {ctx.guild.name}'
|
||||||
|
if requests:
|
||||||
|
for request in requests:
|
||||||
|
member = discord.utils.get(ctx.guild.members, id=request[1])
|
||||||
|
admin = discord.utils.get(ctx.guild.members, id=request[2])
|
||||||
|
title = f"{'Request ID':^12}{'Requested By':^20}{'Assigned to':^20}\n" + \
|
||||||
|
f"{request[0]:^12}{member.display_name if member else 'None':^20}" + \
|
||||||
|
f"{admin.display_name if admin else 'None':^20}"
|
||||||
|
em.add_field(name='',
|
||||||
|
value=f"```{title} \n\n"
|
||||||
|
f"{request[4]}\n\n"
|
||||||
|
f"Requested on: {datetime.strftime(request[5], '%Y-%m-%d at %H:%M:%S')} GMT```",
|
||||||
|
inline=False)
|
||||||
|
else:
|
||||||
|
em.add_field(name='There are no pending requests for this guild.', value='', inline=False)
|
||||||
|
else:
|
||||||
|
if checks.check_admin_role(self.bot, ctx, assigned_to)\
|
||||||
|
or checks.check_rcon_role(self.bot, ctx, assigned_to):
|
||||||
|
requests = self.bot.con.all('select * from admin_requests where assigned_to = %(admin_id)s '
|
||||||
|
'and guild_orig = %(guild_id)s and completed_time is null',
|
||||||
|
{'admin_id': assigned_to.id, 'guild_id': ctx.guild.id})
|
||||||
|
em.title = f'Admin help requests assigned to {assigned_to.display_name} in {ctx.guild.name}'
|
||||||
|
if requests:
|
||||||
|
for request in requests:
|
||||||
|
member = discord.utils.get(ctx.guild.members, id=request[1])
|
||||||
|
em.add_field(name=f"Request ID: {request[0]} Requested By:"
|
||||||
|
f"{member.display_name if member else 'None'}",
|
||||||
|
value=f"{request[4]} \n"
|
||||||
|
f"Requested on: {datetime.strftime(request[5], '%Y-%m-%d at %H:%M:%S')} GMT\n"
|
||||||
|
"",
|
||||||
|
inline=False)
|
||||||
|
else:
|
||||||
|
em.add_field(name=f'There are no pending requests for {assigned_to.display_name} on this guild.',
|
||||||
|
value='',
|
||||||
|
inline=False)
|
||||||
|
else:
|
||||||
|
em.title = f'{assigned_to.display_name} is not an admin in this guild.'
|
||||||
|
else:
|
||||||
|
requests = self.bot.con.all('select * from admin_requests where issuing_member_id = %(member_id)s '
|
||||||
|
'and guild_orig = %(guild_id)s and completed_time is null',
|
||||||
|
{'member_id': ctx.author.id, 'guild_id': ctx.guild.id})
|
||||||
|
em.title = f'Admin help requests for {ctx.author.display_name}'
|
||||||
|
if requests:
|
||||||
|
for request in requests:
|
||||||
|
admin = discord.utils.get(ctx.guild.members, id=request[2])
|
||||||
|
em.add_field(name=f"Request ID: {request[0]}"
|
||||||
|
f"{' Assigned to: ' + admin.display_name if admin else ''}",
|
||||||
|
value=f"{request[4]}\n"
|
||||||
|
f"Requested on: {datetime.strftime(request[5], '%Y-%m-%d at %H:%M:%S')} GMT\n"
|
||||||
|
"",
|
||||||
|
inline=False)
|
||||||
|
else:
|
||||||
|
em.add_field(name='You have no pending Admin Help requests.',
|
||||||
|
value='To submit a request please use `admin new <message>`',
|
||||||
|
inline=False)
|
||||||
|
await ctx.send(embed=em)
|
||||||
|
|
||||||
|
@admin.command(name='close')
|
||||||
|
async def close_request(self, ctx, *, request_ids=None):
|
||||||
|
"""Allows Admin to close admin help tickets.
|
||||||
|
[request_id] must be a valid integer pointing to an open Request ID
|
||||||
|
"""
|
||||||
|
if checks.is_admin(self.bot, ctx) or checks.is_rcon_admin(self.bot, ctx):
|
||||||
|
if request_ids:
|
||||||
|
request_ids = request_ids.replace(' ', '').split(',')
|
||||||
|
for request_id in request_ids:
|
||||||
|
try:
|
||||||
|
request_id = int(request_id)
|
||||||
|
except ValueError:
|
||||||
|
await ctx.send(f'{request_id} is not a valid request id.')
|
||||||
|
else:
|
||||||
|
request = self.bot.con.one(f'select * from admin_requests where id = %(request_id)s',
|
||||||
|
{'request_id': request_id})
|
||||||
|
if request:
|
||||||
|
if request[3] == ctx.guild.id:
|
||||||
|
if request[6] is None:
|
||||||
|
self.bot.con.run('update admin_requests set completed_time = %(time_now)s where '
|
||||||
|
'id = %(request_id)s',
|
||||||
|
{'time_now': ctx.message.created_at, 'request_id': request_id})
|
||||||
|
await ctx.send(f'Request {request_id} by '
|
||||||
|
f'{ctx.guild.get_member(request[1]).display_name}'
|
||||||
|
f' has been marked complete.')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'Request {request_id} is already marked complete.')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'Request {request_id} is not registered to this guild.')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'{request_id} is not a valid request id.')
|
||||||
|
else:
|
||||||
|
await ctx.send('You must include at least one request id to close.')
|
||||||
|
else:
|
||||||
|
await ctx.send(f'You are not authorized to run this command.')
|
||||||
|
|
||||||
|
@commands.command(name='weather', aliases=['wu'])
|
||||||
|
@commands.cooldown(5, 15, type=commands.BucketType.default)
|
||||||
|
async def get_weather(self, ctx, *, location='palmer ak'):
|
||||||
|
"""Gets the weather data for the location provided,
|
||||||
|
If no location is included then it will get the weather for the Bot's home location.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
url = f'http://autocomplete.wunderground.com/aq?query={location}&format=JSON'
|
||||||
|
with async_timeout.timeout(10):
|
||||||
|
async with self.bot.aio_session.get(url) as response:
|
||||||
|
data = await response.json()
|
||||||
|
link = data['RESULTS'][0]['l']
|
||||||
|
url = f'http://api.wunderground.com/api/88e14343b2dd6d8e/geolookup/conditions/{link}.json'
|
||||||
|
with async_timeout.timeout(10):
|
||||||
|
async with self.bot.aio_session.get(url) as response:
|
||||||
|
data = json.loads(await response.text())
|
||||||
|
utils_log.info(data)
|
||||||
|
em = discord.Embed()
|
||||||
|
em.title = f"Weather for {data['current_observation']['display_location']['full']}"
|
||||||
|
em.url = data['current_observation']['forecast_url']
|
||||||
|
em.description = data['current_observation']['observation_time']
|
||||||
|
em.set_thumbnail(url=data['current_observation']['icon_url'])
|
||||||
|
em.set_footer(text='Data provided by wunderground.com',
|
||||||
|
icon_url=data['current_observation']['image']['url'])
|
||||||
|
value_str = f'''```
|
||||||
|
{'Temp:':<20}{data['current_observation']['temperature_string']:<22}
|
||||||
|
{'Feels Like:':<20}{data['current_observation']['feelslike_string']:<22}
|
||||||
|
{'Relative Humidity:':<20}{data['current_observation']['relative_humidity']:<22}
|
||||||
|
{'Wind:':<5}{data['current_observation']['wind_string']:^44}
|
||||||
|
```'''
|
||||||
|
em.add_field(name=f"Current Conditions: {data['current_observation']['weather']}",
|
||||||
|
value=value_str,
|
||||||
|
inline=False)
|
||||||
|
await ctx.send(embed=em)
|
||||||
|
except IndexError:
|
||||||
|
await ctx.send('Can\'t find that location, please try again.')
|
||||||
|
|
||||||
|
@commands.command(name='localtime', aliases=['time', 'lt'])
|
||||||
|
@commands.cooldown(1, 3, type=commands.BucketType.user)
|
||||||
|
async def get_localtime(self, ctx, timezone: str='Anchorage'):
|
||||||
|
em = discord.Embed()
|
||||||
|
try:
|
||||||
|
tz = pytz.timezone(timezone)
|
||||||
|
localtime = datetime.now(tz=tz)
|
||||||
|
em.title = f'{clock_emojis[(localtime.hour % 12)]} {tz}'
|
||||||
|
em.description = localtime.strftime('%c')
|
||||||
|
em.colour = embed_color
|
||||||
|
await ctx.send(embed=em)
|
||||||
|
except pytz.exceptions.UnknownTimeZoneError:
|
||||||
|
for tz in pytz.all_timezones:
|
||||||
|
if timezone.lower() in tz.lower():
|
||||||
|
localtime = datetime.now(tz=pytz.timezone(tz))
|
||||||
|
em.title = f'{clock_emojis[(localtime.hour % 12)]} {tz}'
|
||||||
|
em.description = localtime.strftime('%c')
|
||||||
|
em.colour = embed_color
|
||||||
|
await ctx.send(embed=em)
|
||||||
|
return
|
||||||
|
em.title = 'Unknown Timezone.'
|
||||||
|
em.colour = discord.Colour.red()
|
||||||
|
await ctx.send(embed=em)
|
||||||
|
|
||||||
|
@commands.command(name='purge', aliases=['clean', 'erase'])
|
||||||
|
@commands.cooldown(1, 3, type=commands.BucketType.user)
|
||||||
|
async def purge_messages(self, ctx, number: int=20, member: discord.Member=None):
|
||||||
|
def is_me(message):
|
||||||
|
if message.author == self.bot.user:
|
||||||
|
return True
|
||||||
|
prefixes = self.bot.con.one('select prefix from guild_config where guild_id = %(id)s', {'id': ctx.guild.id})
|
||||||
|
if prefixes:
|
||||||
|
for prefix in prefixes:
|
||||||
|
if message.content.startswith(prefix):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
return message.content.startswith(self.bot.default_prefix)
|
||||||
|
|
||||||
|
def is_member(message):
|
||||||
|
return message.author == member
|
||||||
|
|
||||||
|
def is_author(message):
|
||||||
|
return message.author == ctx.author
|
||||||
|
|
||||||
|
if checks.is_admin(self.bot, ctx):
|
||||||
|
if member:
|
||||||
|
deleted = await ctx.channel.purge(limit=number, check=is_member)
|
||||||
|
if member != ctx.author:
|
||||||
|
await ctx.message.delete()
|
||||||
|
else:
|
||||||
|
deleted = await ctx.channel.purge(limit=number, check=is_me)
|
||||||
|
else:
|
||||||
|
deleted = await ctx.channel.purge(limit=number, check=is_author)
|
||||||
|
em = discord.Embed(title='❌ Purge', colour=discord.Colour.red())
|
||||||
|
em.description = f'Deleted {len(deleted)} messages.'
|
||||||
|
await ctx.send(embed=em, delete_after=5)
|
||||||
|
|
||||||
|
@commands.command(name='purge_all', aliases=['cls', 'clear'])
|
||||||
|
@commands.cooldown(1, 3, type=commands.BucketType.user)
|
||||||
|
async def purge_all(self, ctx, number: int=20, contents: str='all'):
|
||||||
|
if checks.is_admin(self.bot, ctx):
|
||||||
|
if contents != 'all':
|
||||||
|
deleted = await ctx.channel.purge(limit=number, check=lambda message: message.content == contents)
|
||||||
|
else:
|
||||||
|
deleted = await ctx.channel.purge(limit=number)
|
||||||
|
em = discord.Embed(title='❌ Purge', colour=discord.Colour.red())
|
||||||
|
em.description = f'Deleted {len(deleted)} messages.'
|
||||||
|
if contents != ctx.message.content and contents != 'all':
|
||||||
|
await ctx.message.delete()
|
||||||
|
await ctx.send(embed=em, delete_after=5)
|
||||||
|
|
||||||
|
@commands.command(name='google', aliases=['g', 'search'])
|
||||||
|
async def google_search(self, ctx, *, search):
|
||||||
|
res = self.bot.gcs_service.cse().list(q=search, cx=self.bot.bot_secrets['cx']).execute()
|
||||||
|
results = res['items'][:4]
|
||||||
|
em = discord.Embed()
|
||||||
|
em.title = f'Google Search'
|
||||||
|
em.description = f'Top 4 results for "{search}"'
|
||||||
|
em.colour = embed_color
|
||||||
|
for result in results:
|
||||||
|
em.add_field(name=f'{result["title"]}', value=f'{result["snippet"]}\n{result["link"]}')
|
||||||
|
await ctx.send(embed=em)
|
||||||
|
|
||||||
|
@commands.command(hidden=True, name='sheets')
|
||||||
|
async def google_sheets(self, ctx, member: discord.Member):
|
||||||
|
if checks.is_admin(self.bot, ctx):
|
||||||
|
scope = ['https://spreadsheets.google.com/feeds',
|
||||||
|
'https://www.googleapis.com/auth/drive']
|
||||||
|
credentials = ServiceAccountCredentials.from_json_keyfile_name('config/google_client_secret.json', scope)
|
||||||
|
gc = gspread.authorize(credentials)
|
||||||
|
sh = gc.open_by_key(self.bot.bot_secrets['sheet'])
|
||||||
|
ws = sh.worksheet('Current Whitelist')
|
||||||
|
names = ws.col_values('3')
|
||||||
|
steam = ws.col_values('6')
|
||||||
|
tier = ws.col_values('5')
|
||||||
|
patron = ws.col_values('4')
|
||||||
|
em = discord.Embed()
|
||||||
|
em.title = f'User Data from Whitelist Sheet'
|
||||||
|
em.colour = embed_color
|
||||||
|
for i, name in enumerate(names):
|
||||||
|
if member.name.lower() in name.lower()\
|
||||||
|
or member.display_name.lower() in name.lower()\
|
||||||
|
or name.lower() in member.name.lower()\
|
||||||
|
or name.lower() in member.display_name.lower():
|
||||||
|
em.add_field(name=name,
|
||||||
|
value=f'Steam ID: {steam[i]}\nPatreon Level: {tier[i]}\nPatron of: {patron[i]}')
|
||||||
|
await ctx.send(embed=em)
|
||||||
|
|
||||||
|
|
||||||
|
def setup(bot):
|
||||||
|
bot.add_cog(Utils(bot))
|
||||||
+158
@@ -0,0 +1,158 @@
|
|||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
from discord.ext import commands
|
||||||
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
import json
|
||||||
|
import aiohttp
|
||||||
|
from postgres import Postgres
|
||||||
|
from collections import deque
|
||||||
|
from googleapiclient.discovery import build
|
||||||
|
|
||||||
|
log_format = '{asctime}.{msecs:03.0f}|{levelname:<8}|{name}::{message}'
|
||||||
|
date_format = '%Y.%m.%d %H.%M.%S'
|
||||||
|
|
||||||
|
log_dir = 'logs'
|
||||||
|
|
||||||
|
log_file = '{0}/geeksbot_{1}.log'.format(log_dir, datetime.now().strftime('%Y%m%d_%H%M%S%f'))
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.DEBUG, style='{', filename=log_file, datefmt=date_format, format=log_format)
|
||||||
|
console_handler = logging.StreamHandler()
|
||||||
|
console_handler.setLevel(logging.INFO)
|
||||||
|
formatter = logging.Formatter(log_format, style='{', datefmt=date_format)
|
||||||
|
console_handler.setFormatter(formatter)
|
||||||
|
logging.getLogger('').addHandler(console_handler)
|
||||||
|
|
||||||
|
config_dir = 'config/'
|
||||||
|
admin_id_file = 'admin_ids'
|
||||||
|
extension_dir = 'exts'
|
||||||
|
owner_id = 351794468870946827
|
||||||
|
bot_config_file = 'bot_config.json'
|
||||||
|
secrets_file = 'bot_secrets.json'
|
||||||
|
profane_words_file = 'profane_words'
|
||||||
|
|
||||||
|
emojis: Dict[str, str] = {
|
||||||
|
'x': '❌',
|
||||||
|
'y': '✅',
|
||||||
|
'poop': '💩',
|
||||||
|
}
|
||||||
|
|
||||||
|
description = 'I am Geeksbot v0.1! Fear me!'
|
||||||
|
|
||||||
|
|
||||||
|
class Geeksbot(commands.Bot):
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
kwargs["command_prefix"] = self.get_custom_prefix
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.aio_session = aiohttp.ClientSession(loop=self.loop)
|
||||||
|
with open(f'{config_dir}{bot_config_file}') as file:
|
||||||
|
self.bot_config = json.load(file)
|
||||||
|
with open(f'{config_dir}{secrets_file}') as file:
|
||||||
|
self.bot_secrets = json.load(file)
|
||||||
|
# with open(f'{config_dir}{profane_words_file}') as file:
|
||||||
|
# self.profane_words = file.readlines()
|
||||||
|
self.guild_config = {}
|
||||||
|
self.infected = {}
|
||||||
|
self.TOKEN = self.bot_secrets['token']
|
||||||
|
del self.bot_secrets['token']
|
||||||
|
self.con = Postgres(f" host={self.bot_secrets['db_con']['host']}\
|
||||||
|
port={self.bot_secrets['db_con']['port']}\
|
||||||
|
dbname={self.bot_secrets['db_con']['db_name']}\
|
||||||
|
connect_timeout=10 user={self.bot_secrets['db_con']['user']}\
|
||||||
|
password={self.bot_secrets['db_con']['password']}")
|
||||||
|
del self.bot_secrets['db_con']
|
||||||
|
self.default_prefix = 'g$'
|
||||||
|
self.voice_chans = {}
|
||||||
|
self.spam_list = {}
|
||||||
|
self.gcs_service = build('customsearch', 'v1', developerKey=self.bot_secrets['google_search_key'])
|
||||||
|
|
||||||
|
async def get_custom_prefix(self, bot_inst, message):
|
||||||
|
return self.con.one('select prefix from guild_config where guild_id = %(id)s', {'id': message.guild.id})\
|
||||||
|
or self.default_prefix
|
||||||
|
|
||||||
|
async def load_ext(self, ctx, mod=None):
|
||||||
|
self.load_extension('{0}.{1}'.format(extension_dir, mod))
|
||||||
|
if ctx is not None:
|
||||||
|
await ctx.send('{0} loaded.'.format(mod))
|
||||||
|
|
||||||
|
async def unload_ext(self, ctx, mod=None):
|
||||||
|
self.unload_extension('{0}.{1}'.format(extension_dir, mod))
|
||||||
|
if ctx is not None:
|
||||||
|
await ctx.send('{0} unloaded.'.format(mod))
|
||||||
|
|
||||||
|
async def close(self):
|
||||||
|
await super().close()
|
||||||
|
self.aio_session.close() # aiohttp is drunk and can't decide if it's a coro or not
|
||||||
|
|
||||||
|
|
||||||
|
bot = Geeksbot(description=description, case_insensitive=True)
|
||||||
|
|
||||||
|
|
||||||
|
@bot.command(hidden=True)
|
||||||
|
@commands.is_owner()
|
||||||
|
async def load(ctx, mod=None):
|
||||||
|
"""Allows the owner to load extensions dynamically"""
|
||||||
|
await bot.load_ext(ctx, mod)
|
||||||
|
|
||||||
|
|
||||||
|
@bot.command(hidden=True)
|
||||||
|
@commands.is_owner()
|
||||||
|
async def reload(ctx, mod=None):
|
||||||
|
"""Allows the owner to reload extensions dynamically"""
|
||||||
|
if mod == 'all':
|
||||||
|
load_list = bot.bot_config['load_list']
|
||||||
|
for load_item in load_list:
|
||||||
|
await bot.unload_ext(ctx, f'{load_item}')
|
||||||
|
await bot.load_ext(ctx, f'{load_item}')
|
||||||
|
else:
|
||||||
|
await bot.unload_ext(ctx, mod)
|
||||||
|
await bot.load_ext(ctx, mod)
|
||||||
|
|
||||||
|
|
||||||
|
@bot.command(hidden=True)
|
||||||
|
@commands.is_owner()
|
||||||
|
async def unload(ctx, mod):
|
||||||
|
"""Allows the owner to unload extensions dynamically"""
|
||||||
|
await bot.unload_ext(ctx, mod)
|
||||||
|
|
||||||
|
|
||||||
|
@bot.event
|
||||||
|
async def on_message(ctx):
|
||||||
|
if not ctx.author.bot:
|
||||||
|
if ctx.guild:
|
||||||
|
if int(bot.con.one(f"select channel_lockdown from guild_config where guild_id = %(id)s",
|
||||||
|
{'id': ctx.guild.id})):
|
||||||
|
if ctx.channel.id in json.loads(bot.con.one(f"select allowed_channels from guild_config "
|
||||||
|
f"where guild_id = %(id)s",
|
||||||
|
{'id': ctx.guild.id})):
|
||||||
|
await bot.process_commands(ctx)
|
||||||
|
elif ctx.channel.id == 418452585683484680:
|
||||||
|
prefix = bot.con.one('select prefix from guild_config where guild_id = %(id)s', {'id': ctx.guild.id})
|
||||||
|
prefix = prefix[0] if prefix else bot.default_prefix
|
||||||
|
ctx.content = f'{prefix}{ctx.content}'
|
||||||
|
await bot.process_commands(ctx)
|
||||||
|
else:
|
||||||
|
await bot.process_commands(ctx)
|
||||||
|
else:
|
||||||
|
await bot.process_commands(ctx)
|
||||||
|
|
||||||
|
|
||||||
|
@bot.event
|
||||||
|
async def on_ready():
|
||||||
|
bot.recent_msgs = {}
|
||||||
|
for guild in bot.guilds:
|
||||||
|
bot.recent_msgs[guild.id] = deque(maxlen=50)
|
||||||
|
logging.info('Logged in as {0.name}|{0.id}'.format(bot.user))
|
||||||
|
load_list = bot.bot_config['load_list']
|
||||||
|
for load_item in load_list:
|
||||||
|
await bot.load_ext(None, f'{load_item}')
|
||||||
|
logging.info('Extension Loaded: {0}'.format(load_item))
|
||||||
|
logging.info('Done loading, Geeksbot is active.')
|
||||||
|
with open(f'{config_dir}reboot', 'r') as f:
|
||||||
|
reboot = f.readlines()
|
||||||
|
if int(reboot[0]) == 1:
|
||||||
|
await bot.get_channel(int(reboot[1])).send('Restart Finished.')
|
||||||
|
with open(f'{config_dir}reboot', 'w') as f:
|
||||||
|
f.write(f'0')
|
||||||
|
|
||||||
|
bot.run(bot.TOKEN)
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
until python /home/dusty/bin/geeksbot/geeksbot.py; do
|
||||||
|
echo "Geeksbot shutdown with error: $?. Restarting..." >&2
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
module gitea.wayfinderak.com/wayfinderak/geeksbot
|
|
||||||
|
|
||||||
go 1.14
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/bwmarrin/discordgo v0.29.0 // indirect
|
|
||||||
github.com/dustinpianalto/discordgo v0.0.4
|
|
||||||
github.com/dustinpianalto/disgoman v0.0.26
|
|
||||||
github.com/golang-migrate/migrate/v4 v4.14.1
|
|
||||||
github.com/gorcon/rcon v1.3.1
|
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
|
||||||
github.com/lib/pq v1.8.0
|
|
||||||
)
|
|
||||||
@@ -1,622 +0,0 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
|
||||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
|
||||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
|
||||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
|
||||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
|
||||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
|
||||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
|
||||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
|
||||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
|
||||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
|
||||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
|
||||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
|
||||||
cloud.google.com/go v0.63.0/go.mod h1:GmezbQc7T2snqkEXWfZ0sy0VfkB/ivI2DdtJL2DEmlg=
|
|
||||||
cloud.google.com/go v0.64.0/go.mod h1:xfORb36jGvE+6EexW71nMEtL025s3x6xvuYUKM4JLv4=
|
|
||||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
|
||||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
|
||||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
|
||||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
|
||||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
|
||||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
|
||||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
|
||||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
|
||||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
|
||||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
|
||||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
|
||||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
|
||||||
cloud.google.com/go/spanner v1.9.0/go.mod h1:xvlEn0NZ5v1iJPYsBnUVRDNvccDxsBTEi16pJRKQVws=
|
|
||||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
|
||||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
|
||||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
|
||||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
|
||||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
|
||||||
github.com/AlekSi/pointer v1.0.0 h1:KWCWzsvFxNLcmM5XmiqHsGTTsuwZMsLFwWF9Y+//bNE=
|
|
||||||
github.com/AlekSi/pointer v1.0.0/go.mod h1:1kjywbfcPFCmncIxtk6fIEub6LKrfMz3gc5QKVOSOA8=
|
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
|
||||||
github.com/ClickHouse/clickhouse-go v1.3.12/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
|
|
||||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA=
|
|
||||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
|
||||||
github.com/apache/arrow/go/arrow v0.0.0-20200601151325-b2287a20f230/go.mod h1:QNYViu/X0HXDHw7m3KXzWSVXIbfUvJqBFe6Gj8/pYA0=
|
|
||||||
github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
|
||||||
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
|
|
||||||
github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
|
|
||||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
|
||||||
github.com/bwmarrin/discordgo v0.29.0 h1:FmWeXFaKUwrcL3Cx65c20bTRW+vOb6k8AnaP+EgjDno=
|
|
||||||
github.com/bwmarrin/discordgo v0.29.0/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
|
|
||||||
github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg=
|
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
|
||||||
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
|
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
|
||||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
|
||||||
github.com/cockroachdb/cockroach-go v0.0.0-20190925194419-606b3d062051/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk=
|
|
||||||
github.com/containerd/containerd v1.4.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
|
||||||
github.com/containerd/containerd v1.4.1 h1:pASeJT3R3YyVn+94qEPk0SnU1OQ20Jd/T+SPKy9xehY=
|
|
||||||
github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
|
||||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
|
||||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
|
||||||
github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
|
||||||
github.com/dhui/dktest v0.3.3 h1:DBuH/9GFaWbDRa42qsut/hbQu+srAQ0rPWnUoiGX7CA=
|
|
||||||
github.com/dhui/dktest v0.3.3/go.mod h1:EML9sP4sqJELHn4jV7B0TY8oF6077nk83/tz7M56jcQ=
|
|
||||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
|
||||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
|
||||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible h1:iWPIG7pWIsCwT6ZtHnTUpoVMnete7O/pzd9HFE3+tn8=
|
|
||||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
|
||||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
|
||||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
|
||||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
|
||||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
|
||||||
github.com/dustinpianalto/discordgo v0.0.4 h1:KlIkMzDH4jKRbs5IxeqeIXtNTDVcDi/sHV1gaKYEnPg=
|
|
||||||
github.com/dustinpianalto/discordgo v0.0.4/go.mod h1:LKY7UDCPJMChgrowLagHjDSj1gWbt3Hx+6bRFjpaVg8=
|
|
||||||
github.com/dustinpianalto/disgoman v0.0.26 h1:7p6hDbNgEehUgOHigY/lHDY4P2VklJiZmOn674ka1h0=
|
|
||||||
github.com/dustinpianalto/disgoman v0.0.26/go.mod h1:WsDb+7XIm2MJ6xhxRAiXlzSPuSYIGSr4rWRmACeoing=
|
|
||||||
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
|
||||||
github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw=
|
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
|
||||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
|
||||||
github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM=
|
|
||||||
github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0=
|
|
||||||
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
|
||||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
|
||||||
github.com/golang-migrate/migrate/v4 v4.14.1 h1:qmRd/rNGjM1r3Ve5gHd5ZplytrD02UcItYNxJ3iUHHE=
|
|
||||||
github.com/golang-migrate/migrate/v4 v4.14.1/go.mod h1:l7Ks0Au6fYHuUIxUhQ0rcVX1uLlJg54C/VvW7tvxSz0=
|
|
||||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/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/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
|
||||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
|
||||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
|
||||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
|
||||||
github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
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/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
|
||||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
|
||||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
|
||||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
|
||||||
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/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
|
||||||
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/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
|
|
||||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
|
||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
|
||||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
|
||||||
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/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
|
||||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
|
||||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
|
||||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
|
||||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
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/gorcon/rcon v1.3.1 h1:z6a5iOlojfdkvA1qaKEng7QfCJuCzYlC9BUDs6/M+74=
|
|
||||||
github.com/gorcon/rcon v1.3.1/go.mod h1:2gztBPSV2WxkPkqV4jiJkdHs+NT46mNSGb8JxbPesx4=
|
|
||||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
|
||||||
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
|
||||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
|
||||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
|
||||||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
|
||||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
|
||||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
|
||||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
|
||||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
|
||||||
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
|
|
||||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
|
||||||
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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
|
||||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
|
||||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
|
||||||
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
|
||||||
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
|
||||||
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
|
||||||
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
|
||||||
github.com/jackc/pgconn v1.3.2/go.mod h1:LvCquS3HbBKwgl7KbX9KyqEIumJAbm1UMcTvGaIf3bM=
|
|
||||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
|
||||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
|
||||||
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
|
||||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
|
||||||
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
|
||||||
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
|
||||||
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
|
||||||
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
|
||||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
|
||||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
|
||||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
|
||||||
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
|
||||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
|
||||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
|
|
||||||
github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
|
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
|
||||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4=
|
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
|
||||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
|
||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
|
||||||
github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
|
|
||||||
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
|
||||||
github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI=
|
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
|
||||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
|
||||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
|
||||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
|
||||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
|
||||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
|
||||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
|
||||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
|
||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
|
||||||
github.com/mutecomm/go-sqlcipher/v4 v4.4.0/go.mod h1:PyN04SaWalavxRGH9E8ZftG6Ju7rsPrGmQRjrEaVpiY=
|
|
||||||
github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA=
|
|
||||||
github.com/neo4j/neo4j-go-driver v1.8.1-0.20200803113522-b626aa943eba/go.mod h1:ncO5VaFWh0Nrt+4KT4mOZboaczBZcLuHrG+/sUeP8gI=
|
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
|
||||||
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
|
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
|
||||||
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
|
||||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
|
||||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
|
||||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
|
||||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
|
||||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
|
||||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
|
||||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
|
||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
|
||||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
|
||||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
|
||||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
|
||||||
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
|
||||||
github.com/snowflakedb/glog v0.0.0-20180824191149-f5055e6f21ce/go.mod h1:EB/w24pR5VKI60ecFnKqXzxX3dOorz1rnVicQTQrGM0=
|
|
||||||
github.com/snowflakedb/gosnowflake v1.3.5/go.mod h1:13Ky+lxzIm3VqNDZJdyvu9MCGy+WgRdYFdXp96UcLZU=
|
|
||||||
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/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
|
||||||
github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
||||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
|
||||||
github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
|
||||||
github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs=
|
|
||||||
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
|
|
||||||
github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
|
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
|
||||||
gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE=
|
|
||||||
go.mongodb.org/mongo-driver v1.1.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
|
||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
|
||||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
|
||||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
|
||||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
|
||||||
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.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
|
||||||
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-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
|
|
||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
|
||||||
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/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
|
||||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
|
||||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
|
||||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
|
||||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
|
||||||
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/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
|
||||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
|
||||||
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/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
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-20181108082009-03003ca0c849/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-20190225153610-fe579d43d832/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-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/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/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/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/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/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-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/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-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
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 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
|
||||||
golang.org/x/text v0.3.3/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/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
|
|
||||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/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-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/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-20190823170909-c4a336ef6a2f/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-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
|
||||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
|
||||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
|
||||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
|
||||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
|
||||||
golang.org/x/tools v0.0.0-20200806022845-90696ccdc692/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
|
||||||
golang.org/x/tools v0.0.0-20200814230902-9882f1d1823d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
|
||||||
golang.org/x/tools v0.0.0-20200817023811-d00afeaade8f/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
|
||||||
golang.org/x/tools v0.0.0-20200818005847-188abfa75333/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/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/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
|
||||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
|
||||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
|
||||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
|
||||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
|
||||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
|
||||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
|
||||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
|
||||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
|
||||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
|
||||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
|
||||||
google.golang.org/appengine v1.0.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
|
||||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
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/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
|
||||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
|
||||||
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/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
|
||||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
|
||||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
|
||||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
google.golang.org/genproto v0.0.0-20200911024640-645f7a48b24f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
google.golang.org/genproto v0.0.0-20201030142918-24207fddd1c3 h1:sg8vLDNIxFPHTchfhH1E3AI32BL3f23oie38xUWnJM8=
|
|
||||||
google.golang.org/genproto v0.0.0-20201030142918-24207fddd1c3/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
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=
|
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
|
||||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
|
||||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
|
||||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
|
||||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
|
||||||
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
|
||||||
google.golang.org/grpc v1.33.1 h1:DGeFlSan2f+WEtCERJ4J9GJWk15TxUi8QGagfI87Xyc=
|
|
||||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
|
||||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
|
||||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
|
||||||
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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
|
||||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
|
||||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
|
||||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
|
||||||
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.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
|
||||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
|
||||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
|
||||||
modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg=
|
|
||||||
modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8=
|
|
||||||
modernc.org/file v1.0.0/go.mod h1:uqEokAEn1u6e+J45e54dsEA/pw4o7zLrA2GwyntZzjw=
|
|
||||||
modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8=
|
|
||||||
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
|
|
||||||
modernc.org/internal v1.0.0/go.mod h1:VUD/+JAkhCpvkUitlEOnhpVxCgsBI90oTzSCRcqQVSM=
|
|
||||||
modernc.org/lldb v1.0.0/go.mod h1:jcRvJGWfCGodDZz8BPwiKMJxGJngQ/5DrRapkQnLob8=
|
|
||||||
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
|
|
||||||
modernc.org/ql v1.0.0/go.mod h1:xGVyrLIatPcO2C1JvI/Co8c0sr6y91HKFNy4pt9JXEY=
|
|
||||||
modernc.org/sortutil v1.1.0/go.mod h1:ZyL98OQHJgH9IEfN71VsamvJgrtRX9Dj2gX+vH86L1k=
|
|
||||||
modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
|
|
||||||
modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4=
|
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
|
||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package geeksbot
|
|
||||||
|
|
||||||
import "database/sql"
|
|
||||||
|
|
||||||
type Guild struct {
|
|
||||||
ID string
|
|
||||||
NewPatronMessage sql.NullString
|
|
||||||
Prefixes []string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Role struct {
|
|
||||||
ID string
|
|
||||||
RoleType string
|
|
||||||
Guild Guild
|
|
||||||
}
|
|
||||||
|
|
||||||
type GuildService interface {
|
|
||||||
Guild(id string) (Guild, error)
|
|
||||||
CreateGuild(g Guild) (Guild, error)
|
|
||||||
DeleteGuild(g Guild) error
|
|
||||||
UpdateGuild(g Guild) (Guild, error)
|
|
||||||
GuildRoles(g Guild) ([]Role, error)
|
|
||||||
CreateRole(r Role) (Role, error)
|
|
||||||
Role(id string) (Role, error)
|
|
||||||
UpdateRole(r Role) (Role, error)
|
|
||||||
DeleteRole(r Role) error
|
|
||||||
GetOrCreateGuild(id string) (Guild, error)
|
|
||||||
CreateOrUpdateRole(r Role) (Role, error)
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package discord_utils
|
|
||||||
|
|
||||||
import "github.com/dustinpianalto/disgoman"
|
|
||||||
|
|
||||||
func GetChannelName(ctx disgoman.Context, id string) string {
|
|
||||||
channel, err := ctx.Session.Channel(id)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return channel.Name
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
package discord_utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/dustinpianalto/disgoman"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/pkg/services"
|
|
||||||
)
|
|
||||||
|
|
||||||
func IsGuildMod(ctx disgoman.Context, user geeksbot.User) bool {
|
|
||||||
discordCloser, err := ctx.Session.GuildMember(ctx.Guild.ID, user.ID)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
guildRoles, err := services.GuildService.GuildRoles(geeksbot.Guild{ID: ctx.Guild.ID})
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, role := range guildRoles {
|
|
||||||
if role.RoleType == "moderator" {
|
|
||||||
for _, rID := range discordCloser.Roles {
|
|
||||||
if rID == role.ID {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsGuildAdmin(ctx disgoman.Context, user geeksbot.User) bool {
|
|
||||||
discordCloser, err := ctx.Session.GuildMember(ctx.Guild.ID, user.ID)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
guildRoles, err := services.GuildService.GuildRoles(geeksbot.Guild{ID: ctx.Guild.ID})
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, role := range guildRoles {
|
|
||||||
if role.RoleType == "admin" {
|
|
||||||
for _, rID := range discordCloser.Roles {
|
|
||||||
if rID == role.ID {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
package discord_utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ParseDateString(inTime time.Time) string {
|
|
||||||
d := time.Now().Sub(inTime)
|
|
||||||
s := int64(d.Seconds())
|
|
||||||
days := s / 86400
|
|
||||||
s = s - (days * 86400)
|
|
||||||
hours := s / 3600
|
|
||||||
s = s - (hours * 3600)
|
|
||||||
minutes := s / 60
|
|
||||||
seconds := s - (minutes * 60)
|
|
||||||
dateString := ""
|
|
||||||
if days != 0 {
|
|
||||||
dateString += fmt.Sprintf("%v days ", days)
|
|
||||||
}
|
|
||||||
if hours != 0 {
|
|
||||||
dateString += fmt.Sprintf("%v hours ", hours)
|
|
||||||
}
|
|
||||||
if minutes != 0 {
|
|
||||||
dateString += fmt.Sprintf("%v minutes ", minutes)
|
|
||||||
}
|
|
||||||
if seconds != 0 {
|
|
||||||
dateString += fmt.Sprintf("%v seconds ", seconds)
|
|
||||||
}
|
|
||||||
if dateString != "" {
|
|
||||||
dateString += " ago."
|
|
||||||
} else {
|
|
||||||
dateString = "Now"
|
|
||||||
}
|
|
||||||
stamp := inTime.Format("2006-01-02 15:04:05")
|
|
||||||
return fmt.Sprintf("%v\n%v", dateString, stamp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseDurationString(inDur time.Duration) string {
|
|
||||||
s := int64(inDur.Seconds())
|
|
||||||
days := s / 86400
|
|
||||||
s = s - (days * 86400)
|
|
||||||
hours := s / 3600
|
|
||||||
s = s - (hours * 3600)
|
|
||||||
minutes := s / 60
|
|
||||||
seconds := s - (minutes * 60)
|
|
||||||
durString := ""
|
|
||||||
if days != 0 {
|
|
||||||
durString += fmt.Sprintf("%v days ", days)
|
|
||||||
}
|
|
||||||
if hours != 0 {
|
|
||||||
durString += fmt.Sprintf("%v hours ", hours)
|
|
||||||
}
|
|
||||||
if minutes != 0 {
|
|
||||||
durString += fmt.Sprintf("%v minutes ", minutes)
|
|
||||||
}
|
|
||||||
if seconds != 0 {
|
|
||||||
durString += fmt.Sprintf("%v seconds ", seconds)
|
|
||||||
}
|
|
||||||
if durString == "" {
|
|
||||||
durString = "0 seconds"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%v", durString)
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package discord_utils
|
|
||||||
|
|
||||||
import "github.com/dustinpianalto/disgoman"
|
|
||||||
|
|
||||||
func SendErrorMessage(ctx disgoman.Context, msg string, err error) {
|
|
||||||
ctx.CommandManager.ErrorChannel <- disgoman.CommandError{
|
|
||||||
Context: ctx,
|
|
||||||
Message: msg,
|
|
||||||
Error: err,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package discord_utils
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
type Snowflake struct {
|
|
||||||
CreationTime time.Time
|
|
||||||
WorkerID int8
|
|
||||||
ProcessID int8
|
|
||||||
Increment int16
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseSnowflake(s int64) Snowflake {
|
|
||||||
const (
|
|
||||||
DISCORD_EPOCH = 1420070400000
|
|
||||||
TIME_BITS_LOC = 22
|
|
||||||
WORKER_ID_LOC = 17
|
|
||||||
WORKER_ID_MASK = 0x3E0000
|
|
||||||
PROCESS_ID_LOC = 12
|
|
||||||
PROCESS_ID_MASK = 0x1F000
|
|
||||||
INCREMENT_MASK = 0xFFF
|
|
||||||
)
|
|
||||||
creationTime := time.Unix(((s>>TIME_BITS_LOC)+DISCORD_EPOCH)/1000.0, 0)
|
|
||||||
workerID := (s & WORKER_ID_MASK) >> WORKER_ID_LOC
|
|
||||||
processID := (s & PROCESS_ID_MASK) >> PROCESS_ID_LOC
|
|
||||||
increment := s & INCREMENT_MASK
|
|
||||||
return Snowflake{
|
|
||||||
CreationTime: creationTime,
|
|
||||||
WorkerID: int8(workerID),
|
|
||||||
ProcessID: int8(processID),
|
|
||||||
Increment: int16(increment),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package discord_utils
|
|
||||||
|
|
||||||
import "github.com/dustinpianalto/disgoman"
|
|
||||||
|
|
||||||
func GetDisplayName(ctx disgoman.Context, id string) string {
|
|
||||||
member, err := ctx.Session.GuildMember(ctx.Guild.ID, id)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
if member.Nick != "" {
|
|
||||||
return member.Nick
|
|
||||||
}
|
|
||||||
return member.User.Username
|
|
||||||
}
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
package arcon
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/dustinpianalto/disgoman"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/internal/discord_utils"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/pkg/services"
|
|
||||||
"github.com/gorcon/rcon"
|
|
||||||
)
|
|
||||||
|
|
||||||
var ListplayersCommand = &disgoman.Command{
|
|
||||||
Name: "listplayers",
|
|
||||||
Aliases: nil,
|
|
||||||
Description: "List the players currently connected to a ARK server.",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: 0,
|
|
||||||
Invoke: listplayersCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func listplayersCommandFunc(ctx disgoman.Context, args []string) {
|
|
||||||
guild, err := services.GuildService.GetOrCreateGuild(ctx.Guild.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Error getting Guild from the database", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
author, err := services.UserService.GetOrCreateUser(ctx.Message.Author.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Sorry, there was a problem getting your user.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !discord_utils.IsGuildAdmin(ctx, author) && !discord_utils.IsGuildMod(ctx, author) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(args) == 0 {
|
|
||||||
servers, err := services.ServerService.GuildServers(guild)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Could not find any servers for this guild", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, server := range servers {
|
|
||||||
go listplayers(ctx, server)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
serverName := strings.Join(args, " ")
|
|
||||||
server, err := services.ServerService.ServerByName(serverName, guild)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx,
|
|
||||||
fmt.Sprintf("Could not find **%s** in this guild.", serverName),
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
listplayers(ctx, server)
|
|
||||||
}
|
|
||||||
|
|
||||||
func listplayers(ctx disgoman.Context, server geeksbot.Server) {
|
|
||||||
msg, err := ctx.Send(fmt.Sprintf("**Getting data for %s**", server.Name))
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "There was an error getting the player list", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
conn, err := rcon.Dial(fmt.Sprintf("%s:%d", server.IPAddr, server.Port), server.Password)
|
|
||||||
if err != nil {
|
|
||||||
_, _ = ctx.Session.ChannelMessageEdit(ctx.Channel.ID, msg.ID,
|
|
||||||
fmt.Sprintf("**Could not open connection to %s**", server.Name),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
response, err := conn.Execute("listplayers")
|
|
||||||
if err != nil {
|
|
||||||
_, _ = ctx.Session.ChannelMessageEdit(ctx.Channel.ID, msg.ID,
|
|
||||||
fmt.Sprintf("**There was a problem getting a response from %s**", server.Name),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(response, "No Players") {
|
|
||||||
_, _ = ctx.Session.ChannelMessageEdit(ctx.Channel.ID, msg.ID,
|
|
||||||
fmt.Sprintf("**%s: %s**", server.Name, response),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
players := strings.Split(response, "\n")
|
|
||||||
for i, player := range players {
|
|
||||||
parts := strings.Split(player, ", ")
|
|
||||||
steamID := parts[len(parts)-1]
|
|
||||||
user, err := services.UserService.GetBySteamID(steamID)
|
|
||||||
if err == nil {
|
|
||||||
duser, err := ctx.Session.GuildMember(ctx.Guild.ID, user.ID)
|
|
||||||
if err == nil {
|
|
||||||
players[i] = fmt.Sprintf("%s (%s)", player, duser.Mention())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, _ = ctx.Session.ChannelMessageEdit(ctx.Channel.ID, msg.ID,
|
|
||||||
fmt.Sprintf("**%s:**%s", server.Name, strings.Join(players, "\n")),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
var BroadcastCommand = &disgoman.Command{
|
|
||||||
Name: "broadcast",
|
|
||||||
Aliases: nil,
|
|
||||||
Description: "Broadcast a message to ARK servers.",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: 0,
|
|
||||||
Invoke: broadcastCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func broadcastCommandFunc(ctx disgoman.Context, args []string) {
|
|
||||||
guild, err := services.GuildService.GetOrCreateGuild(ctx.Guild.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Error getting Guild from the database", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
author, err := services.UserService.GetOrCreateUser(ctx.Message.Author.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Sorry, there was a problem getting your user.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !discord_utils.IsGuildAdmin(ctx, author) && !discord_utils.IsGuildMod(ctx, author) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
message := strings.Join(args[1:len(args)], " ")
|
|
||||||
if strings.ToLower(args[0]) == "all" {
|
|
||||||
servers, err := services.ServerService.GuildServers(guild)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Could not find any servers for this guild", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, server := range servers {
|
|
||||||
go broadcast(ctx, server, message)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
serverName := strings.Title(strings.ReplaceAll(args[0], "_", " "))
|
|
||||||
server, err := services.ServerService.ServerByName(serverName, guild)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, fmt.Sprintf("Could not find %s", serverName), err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
broadcast(ctx, server, message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func broadcast(ctx disgoman.Context, server geeksbot.Server, message string) {
|
|
||||||
msg, err := ctx.Send(fmt.Sprintf("**Broadcasting to: %s**", server.Name))
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "There was an error getting the player list", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
conn, err := rcon.Dial(fmt.Sprintf("%s:%d", server.IPAddr, server.Port), server.Password)
|
|
||||||
if err != nil {
|
|
||||||
_, _ = ctx.Session.ChannelMessageEdit(ctx.Channel.ID, msg.ID,
|
|
||||||
fmt.Sprintf("**Could not open connection to %s**", server.Name),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
userName := discord_utils.GetDisplayName(ctx, ctx.Message.Author.ID)
|
|
||||||
response, err := conn.Execute(fmt.Sprintf("broadcast %s: %s", userName, message))
|
|
||||||
if err != nil {
|
|
||||||
_, _ = ctx.Session.ChannelMessageEdit(ctx.Channel.ID, msg.ID,
|
|
||||||
fmt.Sprintf("**There was a problem getting a response from %s**", server.Name),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Printf("%T - %#v", response, response)
|
|
||||||
if strings.Contains(response, "Server received, But no response!!") {
|
|
||||||
_, _ = ctx.Session.ChannelMessageEdit(ctx.Channel.ID, msg.ID,
|
|
||||||
fmt.Sprintf("**%s Broadcast Successful**", server.Name),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, _ = ctx.Session.ChannelMessageEdit(ctx.Channel.ID, msg.ID,
|
|
||||||
fmt.Sprintf("**Broadcasting to %s Failed!**", server.Name),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
package guild
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/dustinpianalto/disgoman"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/internal/discord_utils"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/internal/utils"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/pkg/services"
|
|
||||||
)
|
|
||||||
|
|
||||||
var AddPrefixCommand = &disgoman.Command{
|
|
||||||
Name: "addPrefix",
|
|
||||||
Aliases: []string{"ap"},
|
|
||||||
Description: "Add a prefix for use in this guild.",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: disgoman.PermissionManageServer,
|
|
||||||
Invoke: addPrefixCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func addPrefixCommandFunc(ctx disgoman.Context, args []string) {
|
|
||||||
if len(args) == 0 {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Please include at least one prefix to add", nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guild, err := services.GuildService.Guild(ctx.Guild.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Guild not configured, adding new guild to the database", nil)
|
|
||||||
guild = geeksbot.Guild{
|
|
||||||
ID: ctx.Guild.ID,
|
|
||||||
Prefixes: args,
|
|
||||||
}
|
|
||||||
guild, err = services.GuildService.CreateGuild(guild)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Error adding guild to database.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
guild.Prefixes = append(guild.Prefixes, args...)
|
|
||||||
guild.Prefixes = utils.RemoveDuplicateStrings(guild.Prefixes)
|
|
||||||
guild, err = services.GuildService.UpdateGuild(guild)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Error adding prefixes to guild.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, err = ctx.Send(fmt.Sprintf("Prefixes Updates.\nThe Prefixes for this guild are currently %#v", guild.Prefixes))
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Error sending update message", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var RemovePrefixCommand = &disgoman.Command{
|
|
||||||
Name: "removePrefix",
|
|
||||||
Aliases: []string{"rp"},
|
|
||||||
Description: "Remove a prefix so it can't be used in this guild",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: disgoman.PermissionManageServer,
|
|
||||||
Invoke: removePrefixCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func removePrefixCommandFunc(ctx disgoman.Context, args []string) {
|
|
||||||
if len(args) == 0 {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Please include at least one prefix to remove", nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guild, err := services.GuildService.Guild(ctx.Guild.ID)
|
|
||||||
var removed []string
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Guild not configured, adding new guild to the database", nil)
|
|
||||||
guild = geeksbot.Guild{
|
|
||||||
ID: ctx.Guild.ID,
|
|
||||||
Prefixes: []string{},
|
|
||||||
}
|
|
||||||
guild, err = services.GuildService.CreateGuild(guild)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Error adding guild to database.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for _, a := range args {
|
|
||||||
l := len(guild.Prefixes)
|
|
||||||
for i := 0; i < l; i++ {
|
|
||||||
if a == guild.Prefixes[i] {
|
|
||||||
guild.Prefixes = append(guild.Prefixes[:i], guild.Prefixes[i+1:]...)
|
|
||||||
l--
|
|
||||||
i--
|
|
||||||
removed = append(removed, a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
removed = utils.RemoveDuplicateStrings(removed)
|
|
||||||
guild.Prefixes = utils.RemoveDuplicateStrings(guild.Prefixes)
|
|
||||||
guild, err = services.GuildService.UpdateGuild(guild)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Error removing prefixes from guild.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, err = ctx.Send(fmt.Sprintf("Prefixes Updates.\n"+
|
|
||||||
"The Prefixes for this guild are currently %#v\n"+
|
|
||||||
"Removed: %#v", guild.Prefixes, removed))
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Error sending update message", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,404 +0,0 @@
|
|||||||
package guild
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/dustinpianalto/discordgo"
|
|
||||||
"github.com/dustinpianalto/disgoman"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/internal/discord_utils"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/internal/utils"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/pkg/services"
|
|
||||||
)
|
|
||||||
|
|
||||||
var AddModeratorRoleCommand = &disgoman.Command{
|
|
||||||
Name: "addMod",
|
|
||||||
Aliases: []string{"addModerator", "addModRole"},
|
|
||||||
Description: "Add a role which is allowed to run moderator commands",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: disgoman.PermissionManageServer,
|
|
||||||
Invoke: addModeratorRoleCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func addModeratorRoleCommandFunc(ctx disgoman.Context, args []string) {
|
|
||||||
var count int
|
|
||||||
added := make(map[string]bool)
|
|
||||||
guild, err := services.GuildService.GetOrCreateGuild(ctx.Guild.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Something went wrong getting the guild", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
roles := append(args, ctx.Message.MentionRoles...)
|
|
||||||
if len(roles) > 0 {
|
|
||||||
for _, id := range roles {
|
|
||||||
if strings.HasPrefix(id, "<@&") && strings.HasSuffix(id, ">") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, ok := added[id]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, err = ctx.Session.State.Role(ctx.Guild.ID, id); err != nil {
|
|
||||||
_, _ = ctx.Send(fmt.Sprintf("%s does not reference a valid role for this guild.", id))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
_, err := services.GuildService.CreateOrUpdateRole(geeksbot.Role{
|
|
||||||
ID: id,
|
|
||||||
RoleType: "moderator",
|
|
||||||
Guild: guild,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, fmt.Sprintf("There was a problem adding <@&%s>", id), err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
added[id] = true
|
|
||||||
count++
|
|
||||||
_, _ = ctx.Send(fmt.Sprintf("Added <@&%s> as a moderator role.", id))
|
|
||||||
}
|
|
||||||
_, _ = ctx.Send(fmt.Sprintf("Added %d moderator %s.", count, utils.PluralizeString("role", count)))
|
|
||||||
} else {
|
|
||||||
_, _ = ctx.Send("Please include at least one role to make a moderator role.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var AddAdminRoleCommand = &disgoman.Command{
|
|
||||||
Name: "addAdmin",
|
|
||||||
Aliases: []string{"addAdminRole"},
|
|
||||||
Description: "Add a role which is allowed to run admin commands",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: disgoman.PermissionManageServer,
|
|
||||||
Invoke: addAdminRoleCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func addAdminRoleCommandFunc(ctx disgoman.Context, args []string) {
|
|
||||||
var count int
|
|
||||||
added := make(map[string]bool)
|
|
||||||
guild, err := services.GuildService.GetOrCreateGuild(ctx.Guild.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Something went wrong getting the guild", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
roles := append(args, ctx.Message.MentionRoles...)
|
|
||||||
if len(roles) > 0 {
|
|
||||||
for _, id := range roles {
|
|
||||||
if strings.HasPrefix(id, "<@&") && strings.HasSuffix(id, ">") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, ok := added[id]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, err = ctx.Session.State.Role(ctx.Guild.ID, id); err != nil {
|
|
||||||
_, _ = ctx.Send(fmt.Sprintf("%s does not reference a valid role for this guild.", id))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
_, err := services.GuildService.CreateOrUpdateRole(geeksbot.Role{
|
|
||||||
ID: id,
|
|
||||||
RoleType: "admin",
|
|
||||||
Guild: guild,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, fmt.Sprintf("There was a problem adding <@&%s>", id), err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
added[id] = true
|
|
||||||
count++
|
|
||||||
_, _ = ctx.Send(fmt.Sprintf("Added <@&%s> as an admin role.", id))
|
|
||||||
}
|
|
||||||
_, _ = ctx.Send(fmt.Sprintf("Added %d admin %s.", count, utils.PluralizeString("role", count)))
|
|
||||||
} else {
|
|
||||||
_, _ = ctx.Send("Please include at least one role to make an admin role.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var RemoveModRoleCommand = &disgoman.Command{
|
|
||||||
Name: "removeMod",
|
|
||||||
Aliases: []string{"removeModeratorRole", "removeModRole", "removeAdmin", "removeAdminRole"},
|
|
||||||
Description: "Remove a role or several roles from the moderator or admin list",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: disgoman.PermissionManageServer,
|
|
||||||
Invoke: removeModRoleCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeModRoleCommandFunc(ctx disgoman.Context, args []string) {
|
|
||||||
var count int
|
|
||||||
added := make(map[string]bool)
|
|
||||||
guild, err := services.GuildService.GetOrCreateGuild(ctx.Guild.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Something went wrong getting the guild", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
roles := append(args, ctx.Message.MentionRoles...)
|
|
||||||
if len(roles) > 0 {
|
|
||||||
for _, id := range roles {
|
|
||||||
if strings.HasPrefix(id, "<@&") && strings.HasSuffix(id, ">") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, ok := added[id]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, err = ctx.Session.State.Role(ctx.Guild.ID, id); err != nil {
|
|
||||||
if r, err := services.GuildService.Role(id); err != nil {
|
|
||||||
_, _ = ctx.Send(fmt.Sprintf("%s does not reference a valid role for this guild.", id))
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
err = services.GuildService.DeleteRole(r)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Something went wrong deleting the role", err)
|
|
||||||
}
|
|
||||||
_, _ = ctx.Send(fmt.Sprintf("Deleted <@&%s> as a no longer a valid role.", id))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, err := services.GuildService.CreateOrUpdateRole(geeksbot.Role{
|
|
||||||
ID: id,
|
|
||||||
RoleType: "normal",
|
|
||||||
Guild: guild,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, fmt.Sprintf("There was a problem updating <@&%s>", id), err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
added[id] = true
|
|
||||||
count++
|
|
||||||
_, _ = ctx.Send(fmt.Sprintf("Set <@&%s> as a normal role.", id))
|
|
||||||
}
|
|
||||||
_, _ = ctx.Send(fmt.Sprintf("Set %d %s to normal.", count, utils.PluralizeString("role", count)))
|
|
||||||
} else {
|
|
||||||
_, _ = ctx.Send("Please include at least one role to remove from the moderator or admin lists.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var MakeRoleSelfAssignableCommand = &disgoman.Command{
|
|
||||||
Name: "make-role-self-assignable",
|
|
||||||
Aliases: []string{"makesar", "addsar"},
|
|
||||||
Description: "Makes the passed in role self assignable by anyone",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: disgoman.PermissionManageServer,
|
|
||||||
Invoke: makeRoleSelfAssignableCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeRoleSelfAssignableCommandFunc(ctx disgoman.Context, args []string) {
|
|
||||||
added := make(map[string]bool)
|
|
||||||
guild, err := services.GuildService.GetOrCreateGuild(ctx.Guild.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Something went wrong getting the guild", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
roles := append(args, ctx.Message.MentionRoles...)
|
|
||||||
if len(roles) > 0 {
|
|
||||||
for _, id := range roles {
|
|
||||||
if strings.HasPrefix(id, "<@&") && strings.HasSuffix(id, ">") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, ok := added[id]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var role *discordgo.Role
|
|
||||||
var err error
|
|
||||||
if role, err = ctx.Session.State.Role(ctx.Guild.ID, id); err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, fmt.Sprintf("%s does not reference a valid role for this guild", id), err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = services.GuildService.CreateOrUpdateRole(geeksbot.Role{
|
|
||||||
ID: role.ID,
|
|
||||||
RoleType: "sar",
|
|
||||||
Guild: guild,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, fmt.Sprintf("There was a problem updating <@&%s>", role.ID), err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, _ = ctx.Send(fmt.Sprintf("%s is now self assignable", role.Name))
|
|
||||||
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_, _ = ctx.Send("Please include at least one role to make self assignable")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var RemoveSelfAssignableCommand = &disgoman.Command{
|
|
||||||
Name: "remove-self-assignable-role",
|
|
||||||
Aliases: []string{"removesar"},
|
|
||||||
Description: "Makes a role that was previously self assignable not so",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: disgoman.PermissionManageServer,
|
|
||||||
Invoke: removeSelfAssignableRoleCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeSelfAssignableRoleCommandFunc(ctx disgoman.Context, args []string) {
|
|
||||||
removed := make(map[string]bool)
|
|
||||||
guild, err := services.GuildService.GetOrCreateGuild(ctx.Guild.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Something went wrong getting the guild", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
roles := append(args, ctx.Message.MentionRoles...)
|
|
||||||
if len(roles) > 0 {
|
|
||||||
for _, id := range roles {
|
|
||||||
if strings.HasPrefix(id, "<@&") && strings.HasSuffix(id, ">") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, ok := removed[id]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var role *discordgo.Role
|
|
||||||
if role, err = ctx.Session.State.Role(ctx.Guild.ID, id); err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, fmt.Sprintf("%s does not reference a valid role for this guild", id), err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err = services.GuildService.CreateOrUpdateRole(geeksbot.Role{
|
|
||||||
ID: role.ID,
|
|
||||||
RoleType: "normal",
|
|
||||||
Guild: guild,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, fmt.Sprintf("There was a problem updating <@&%s>", role.ID), err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, _ = ctx.Send(fmt.Sprintf("%s's self assignability has been removed.", role.Name))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_, _ = ctx.Send("Please include at least one role to make self assignable")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var SelfAssignRoleCommand = &disgoman.Command{
|
|
||||||
Name: "giverole",
|
|
||||||
Aliases: []string{"iwant", "givetome", "addrole"},
|
|
||||||
Description: "Assigns a person the passed in role if it is self assignable",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: 0,
|
|
||||||
Invoke: selfAssignRoleCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func selfAssignRoleCommandFunc(ctx disgoman.Context, args []string) {
|
|
||||||
added := make(map[string]bool)
|
|
||||||
roles := append(args, ctx.Message.MentionRoles...)
|
|
||||||
if len(roles) > 0 {
|
|
||||||
for _, id := range roles {
|
|
||||||
var roleID string
|
|
||||||
if strings.HasPrefix(id, "<@&") && strings.HasSuffix(id, ">") {
|
|
||||||
continue
|
|
||||||
} else if _, err := strconv.Atoi(id); err == nil {
|
|
||||||
roleID = id
|
|
||||||
} else {
|
|
||||||
for _, role := range ctx.Guild.Roles {
|
|
||||||
if strings.ToLower(id) == strings.ToLower(role.Name) {
|
|
||||||
roleID = role.ID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := added[id]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var role *discordgo.Role
|
|
||||||
var err error
|
|
||||||
if role, err = ctx.Session.State.Role(ctx.Guild.ID, roleID); err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, fmt.Sprintf("%s does not reference a valid role for this guild", roleID), err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if memberHasRole(ctx.Member, role.ID) {
|
|
||||||
_, _ = ctx.Send(fmt.Sprintf("You already have the %s role silly...", role.Name))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r, err := services.GuildService.Role(role.ID)
|
|
||||||
if err != nil || r.RoleType != "sar" {
|
|
||||||
discord_utils.SendErrorMessage(ctx, fmt.Sprintf("You aren't allowed to assign yourself the %s role", role.Name), err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = ctx.Session.GuildMemberRoleAdd(ctx.Guild.ID, ctx.User.ID, role.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "There was a problem adding that role to you.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, _ = ctx.Send(fmt.Sprintf("Congratulations! The %s role has been added to your... Ummm... Thing.", role.Name))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_, _ = ctx.Send("Please include at least one role to make self assignable")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var UnAssignRoleCommand = &disgoman.Command{
|
|
||||||
Name: "removerole",
|
|
||||||
Aliases: []string{"idon'twant"},
|
|
||||||
Description: "Removes a role from a person if the role is self assignable",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: 0,
|
|
||||||
Invoke: unAssignRoleCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func unAssignRoleCommandFunc(ctx disgoman.Context, args []string) {
|
|
||||||
removed := make(map[string]bool)
|
|
||||||
roles := append(args, ctx.Message.MentionRoles...)
|
|
||||||
if len(roles) > 0 {
|
|
||||||
for _, id := range roles {
|
|
||||||
var roleID string
|
|
||||||
if strings.HasPrefix(id, "<@&") && strings.HasSuffix(id, ">") {
|
|
||||||
continue
|
|
||||||
} else if _, err := strconv.Atoi(id); err == nil {
|
|
||||||
roleID = id
|
|
||||||
} else {
|
|
||||||
for _, role := range ctx.Guild.Roles {
|
|
||||||
if strings.ToLower(id) == strings.ToLower(role.Name) {
|
|
||||||
roleID = role.ID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, ok := removed[id]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var role *discordgo.Role
|
|
||||||
var err error
|
|
||||||
if role, err = ctx.Session.State.Role(ctx.Guild.ID, roleID); err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, fmt.Sprintf("%s does not reference a valid role for this guild", roleID), err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !memberHasRole(ctx.Member, role.ID) {
|
|
||||||
_, _ = ctx.Send(fmt.Sprintf("I can't remove the %s role from you because you don't have it...", role.Name))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r, err := services.GuildService.Role(role.ID)
|
|
||||||
if err != nil || r.RoleType != "sar" {
|
|
||||||
discord_utils.SendErrorMessage(ctx, fmt.Sprintf("You aren't allowed to assign yourself the %s role", role.Name), err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = ctx.Session.GuildMemberRoleRemove(ctx.Guild.ID, ctx.User.ID, role.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "There was a problem removing that role from your account", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, _ = ctx.Send(fmt.Sprintf("Sad to see you go... but the %s role has been removed.", role.Name))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_, _ = ctx.Send("Please include at least one role to make self assignable")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func memberHasRole(m *discordgo.Member, id string) bool {
|
|
||||||
for _, r := range m.Roles {
|
|
||||||
if r == id {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package exts
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/dustinpianalto/disgoman"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/internal/exts/arcon"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/internal/exts/guild"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/internal/exts/requests"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/internal/exts/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
func AddCommandHandlers(g *disgoman.CommandManager) {
|
|
||||||
// Arguments:
|
|
||||||
// name - command name - string
|
|
||||||
// desc - command description - string
|
|
||||||
// owneronly - only allow owners to run - bool
|
|
||||||
// hidden - hide command from non-owners - bool
|
|
||||||
// perms - permissisions required - anpan.Permission (int)
|
|
||||||
// type - command type, sets where the command is available
|
|
||||||
// run - function to run - func(anpan.Context, []string) / CommandRunFunc
|
|
||||||
_ = g.AddCommand(utils.UserCommand)
|
|
||||||
_ = g.AddCommand(utils.AddUserCommand)
|
|
||||||
_ = g.AddCommand(utils.SayCommand)
|
|
||||||
_ = g.AddCommand(utils.GitCommand)
|
|
||||||
_ = g.AddCommand(utils.InviteCommand)
|
|
||||||
_ = g.AddCommand(utils.PingCommand)
|
|
||||||
_ = g.AddCommand(guild.AddPrefixCommand)
|
|
||||||
_ = g.AddCommand(guild.RemovePrefixCommand)
|
|
||||||
_ = g.AddCommand(guild.AddModeratorRoleCommand)
|
|
||||||
_ = g.AddCommand(guild.AddAdminRoleCommand)
|
|
||||||
_ = g.AddCommand(guild.RemoveModRoleCommand)
|
|
||||||
_ = g.AddCommand(guild.MakeRoleSelfAssignableCommand)
|
|
||||||
_ = g.AddCommand(guild.RemoveSelfAssignableCommand)
|
|
||||||
_ = g.AddCommand(guild.SelfAssignRoleCommand)
|
|
||||||
_ = g.AddCommand(guild.UnAssignRoleCommand)
|
|
||||||
_ = g.AddCommand(requests.RequestCommand)
|
|
||||||
_ = g.AddCommand(requests.CloseCommand)
|
|
||||||
_ = g.AddCommand(requests.ListCommand)
|
|
||||||
_ = g.AddCommand(requests.ViewCommand)
|
|
||||||
_ = g.AddCommand(requests.CommentCommand)
|
|
||||||
_ = g.AddCommand(arcon.ListplayersCommand)
|
|
||||||
_ = g.AddCommand(arcon.BroadcastCommand)
|
|
||||||
}
|
|
||||||
@@ -1,449 +0,0 @@
|
|||||||
package requests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/dustinpianalto/disgoman"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/internal/discord_utils"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/internal/utils"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/pkg/services"
|
|
||||||
)
|
|
||||||
|
|
||||||
var RequestCommand = &disgoman.Command{
|
|
||||||
Name: "request",
|
|
||||||
Aliases: nil,
|
|
||||||
Description: "Submit a request for the guild staff",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: 0,
|
|
||||||
Invoke: requestCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func requestCommandFunc(ctx disgoman.Context, args []string) {
|
|
||||||
guild, err := services.GuildService.GetOrCreateGuild(ctx.Guild.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Error getting Guild from the database", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
requestMsg := strings.Join(args, " ")
|
|
||||||
author, err := services.UserService.GetOrCreateUser(ctx.Message.Author.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Error creating the request. Could not get user.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
channel, err := services.ChannelService.GetOrCreateChannel(ctx.Message.ChannelID, ctx.Guild.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Error creating the request. Could not get channel.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
int64ID, _ := strconv.ParseInt(ctx.Message.ID, 10, 64)
|
|
||||||
s := discord_utils.ParseSnowflake(int64ID)
|
|
||||||
message, err := services.MessageService.CreateMessage(geeksbot.Message{
|
|
||||||
ID: ctx.Message.ID,
|
|
||||||
CreatedAt: s.CreationTime,
|
|
||||||
Content: ctx.Message.Content,
|
|
||||||
Channel: channel,
|
|
||||||
Author: author,
|
|
||||||
})
|
|
||||||
request := geeksbot.Request{
|
|
||||||
Author: author,
|
|
||||||
Channel: channel,
|
|
||||||
Guild: guild,
|
|
||||||
Content: requestMsg,
|
|
||||||
RequestedAt: s.CreationTime,
|
|
||||||
Completed: false,
|
|
||||||
Message: message,
|
|
||||||
}
|
|
||||||
request, err = services.RequestService.CreateRequest(request)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Error creating the request", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
channels, err := services.ChannelService.GuildChannels(guild)
|
|
||||||
if err == nil {
|
|
||||||
var mentionRolesString string
|
|
||||||
roles, err := services.GuildService.GuildRoles(guild)
|
|
||||||
if err == nil {
|
|
||||||
for _, r := range roles {
|
|
||||||
if r.RoleType == "admin" || r.RoleType == "moderator" {
|
|
||||||
mentionRolesString += fmt.Sprintf("<@&%s> ", r.ID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, c := range channels {
|
|
||||||
if c.Admin {
|
|
||||||
_, _ = ctx.Session.ChannelMessageSend(c.ID,
|
|
||||||
fmt.Sprintf("%s\n"+
|
|
||||||
"New Request ID %d\n"+
|
|
||||||
"%s has requested assistance: \n"+
|
|
||||||
"```\n%s\n```\n"+
|
|
||||||
"Requested At: %s\n"+
|
|
||||||
"In: %s",
|
|
||||||
mentionRolesString,
|
|
||||||
request.ID,
|
|
||||||
ctx.Message.Author.Mention(),
|
|
||||||
request.Content,
|
|
||||||
request.RequestedAt.UTC().Format(time.UnixDate),
|
|
||||||
ctx.Channel.Mention(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, err = ctx.Send(fmt.Sprintf("%s The admin have recieved your request.\n "+
|
|
||||||
"If you would like to close or add a comment to this request please reference ID `%v`",
|
|
||||||
ctx.Message.Author.Mention(), request.ID,
|
|
||||||
))
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "There was an error sending the message. The request was created.", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var CloseCommand = &disgoman.Command{
|
|
||||||
Name: "close",
|
|
||||||
Aliases: nil,
|
|
||||||
Description: "Close a request and mark it as completed.",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: 0,
|
|
||||||
Invoke: closeCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func closeCommandFunc(ctx disgoman.Context, args []string) {
|
|
||||||
var ids []int64
|
|
||||||
var reason string
|
|
||||||
_, err := strconv.ParseInt(args[len(args)-1], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
reason = args[len(args)-1]
|
|
||||||
args = args[0 : len(args)-1]
|
|
||||||
}
|
|
||||||
for _, a := range args {
|
|
||||||
a = strings.Trim(a, ",")
|
|
||||||
id, err := strconv.ParseInt(a, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, fmt.Sprintf("%s is not a valid request id", a), err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ids = append(ids, id)
|
|
||||||
}
|
|
||||||
if len(ids) == 0 {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "No requests to close", nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guild, err := services.GuildService.GetOrCreateGuild(ctx.Guild.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Error getting Guild from the database", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
closer, err := services.UserService.GetOrCreateUser(ctx.Message.Author.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Error closing the request. Could not get user.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
int64ID, _ := strconv.ParseInt(ctx.Message.ID, 10, 64)
|
|
||||||
s := discord_utils.ParseSnowflake(int64ID)
|
|
||||||
for _, id := range ids {
|
|
||||||
request, err := services.RequestService.Request(id)
|
|
||||||
if err != nil || request.Guild.ID != guild.ID {
|
|
||||||
discord_utils.SendErrorMessage(ctx, fmt.Sprintf("%d is not a request in this guild.", id), err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if request.Author != closer && !closer.IsStaff && !closer.IsAdmin {
|
|
||||||
if !discord_utils.IsGuildMod(ctx, closer) && !discord_utils.IsGuildAdmin(ctx, closer) {
|
|
||||||
discord_utils.SendErrorMessage(ctx, fmt.Sprintf("You are not authorized to close %d", id), nil)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if request.Completed {
|
|
||||||
discord_utils.SendErrorMessage(ctx, fmt.Sprintf("%d is already closed", id), err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
request.Completed = true
|
|
||||||
request.CompletedAt.Valid = true
|
|
||||||
request.CompletedAt.Time = s.CreationTime
|
|
||||||
request.CompletedBy = &closer
|
|
||||||
if reason != "" {
|
|
||||||
request.CompletedMessage.Valid = true
|
|
||||||
request.CompletedMessage.String = reason
|
|
||||||
}
|
|
||||||
request, err = services.RequestService.UpdateRequest(request)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, fmt.Sprintf("Error closing %d", id), err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
_, err = ctx.Send(fmt.Sprintf("%d has been closed", request.ID))
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "There was an error sending the message. The request was closed.", err)
|
|
||||||
}
|
|
||||||
dmChannel, err := ctx.Session.UserChannelCreate(request.Author.ID)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, _ = ctx.Session.ChannelMessageSend(dmChannel.ID,
|
|
||||||
fmt.Sprintf("%s has closed request %d which you opened in the %s channel.\n```%s```\n",
|
|
||||||
discord_utils.GetDisplayName(ctx, request.CompletedBy.ID),
|
|
||||||
request.ID,
|
|
||||||
discord_utils.GetChannelName(ctx, request.Channel.ID),
|
|
||||||
request.Content,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ListCommand = &disgoman.Command{
|
|
||||||
Name: "list",
|
|
||||||
Aliases: []string{"request_list", "requests_list"},
|
|
||||||
Description: "List your open requests or all open requests if the caller is a moderator",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: 0,
|
|
||||||
Invoke: listCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func listCommandFunc(ctx disgoman.Context, args []string) {
|
|
||||||
user, err := services.UserService.GetOrCreateUser(ctx.Message.Author.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Error closing the request. Could not get user.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guild, err := services.GuildService.GetOrCreateGuild(ctx.Guild.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Error getting Guild from the database", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var requests []geeksbot.Request
|
|
||||||
if discord_utils.IsGuildMod(ctx, user) || discord_utils.IsGuildAdmin(ctx, user) {
|
|
||||||
requests, err = services.RequestService.GuildRequests(guild, false)
|
|
||||||
} else {
|
|
||||||
requests, err = services.RequestService.UserRequests(user, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, request := range requests {
|
|
||||||
var authorName string
|
|
||||||
author, err := ctx.Session.GuildMember(guild.ID, request.Author.ID)
|
|
||||||
if err != nil {
|
|
||||||
authorName = "Unknown"
|
|
||||||
} else {
|
|
||||||
if author.Nick == "" {
|
|
||||||
authorName = author.User.Username
|
|
||||||
} else {
|
|
||||||
authorName = author.Nick
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var channelName string
|
|
||||||
channel, err := ctx.Session.Channel(request.Channel.ID)
|
|
||||||
if err != nil {
|
|
||||||
channelName = "Unknown"
|
|
||||||
} else {
|
|
||||||
channelName = channel.Name
|
|
||||||
}
|
|
||||||
commentCount, err := services.RequestService.RequestCommentCount(request)
|
|
||||||
if err != nil {
|
|
||||||
commentCount = 0
|
|
||||||
}
|
|
||||||
_, _ = ctx.Send(fmt.Sprintf("```md\n"+
|
|
||||||
"< Request ID Requested By >\n"+
|
|
||||||
"< %-11d %23s >\n"+
|
|
||||||
"%s\n\n"+
|
|
||||||
"Comments: %d\n"+
|
|
||||||
"Requested At: %s\n"+
|
|
||||||
"In: %s\n"+
|
|
||||||
"```",
|
|
||||||
request.ID,
|
|
||||||
authorName,
|
|
||||||
request.Content,
|
|
||||||
commentCount,
|
|
||||||
request.RequestedAt.Format("2006-01-02 15:04:05 MST"),
|
|
||||||
channelName,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _ = ctx.Send(fmt.Sprintf("```There %s currently %d open %s```", utils.PluralizeString("is", len(requests)), len(requests), utils.PluralizeString("request", len(requests))))
|
|
||||||
}
|
|
||||||
|
|
||||||
var CommentCommand = &disgoman.Command{
|
|
||||||
Name: "comment",
|
|
||||||
Aliases: []string{"update", "add_comment"},
|
|
||||||
Description: "Add a comment to an existing request.",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: 0,
|
|
||||||
Invoke: commentCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func commentCommandFunc(ctx disgoman.Context, args []string) {
|
|
||||||
guild, err := services.GuildService.GetOrCreateGuild(ctx.Guild.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Error getting Guild from the database", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
id, err := strconv.ParseInt(args[0], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Please include the ID of the request to update.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
message := strings.Join(args[1:len(args)], " ")
|
|
||||||
author, err := services.UserService.GetOrCreateUser(ctx.Message.Author.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Sorry, there was an issue finding your user account", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
request, err := services.RequestService.Request(id)
|
|
||||||
if err != nil || request.Guild.ID != guild.ID {
|
|
||||||
discord_utils.SendErrorMessage(ctx, fmt.Sprintf("%d is not a valid request for this guild", id), err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
int64ID, _ := strconv.ParseInt(ctx.Message.ID, 10, 64)
|
|
||||||
s := discord_utils.ParseSnowflake(int64ID)
|
|
||||||
comment := geeksbot.Comment{
|
|
||||||
Author: author,
|
|
||||||
Request: request,
|
|
||||||
CommentAt: s.CreationTime,
|
|
||||||
Content: message,
|
|
||||||
}
|
|
||||||
comment, err = services.RequestService.CreateComment(comment)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "There was a problem adding your comment", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
channels, err := services.ChannelService.GuildChannels(guild)
|
|
||||||
if err == nil {
|
|
||||||
comments, _ := services.RequestService.RequestComments(request)
|
|
||||||
var commentString string
|
|
||||||
var commentStrings []string
|
|
||||||
commentString = fmt.Sprintf("Comment added:\n```md\n"+
|
|
||||||
"< Request ID Requested By >\n"+
|
|
||||||
"< %-11d %23s >\n"+
|
|
||||||
"%s\n\n"+
|
|
||||||
"Comments: Not Implemented Yet\n"+
|
|
||||||
"Requested At: %s\n"+
|
|
||||||
"In: %s\n"+
|
|
||||||
"```",
|
|
||||||
request.ID,
|
|
||||||
discord_utils.GetDisplayName(ctx, request.Author.ID),
|
|
||||||
request.Content,
|
|
||||||
request.RequestedAt.Format("2006-01-02 15:04:05"),
|
|
||||||
discord_utils.GetChannelName(ctx, request.Channel.ID),
|
|
||||||
)
|
|
||||||
for _, c := range comments {
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cs := fmt.Sprintf("```md\n%s\n- %s At %s\n```\n",
|
|
||||||
c.Content,
|
|
||||||
discord_utils.GetDisplayName(ctx, c.Author.ID),
|
|
||||||
c.CommentAt.Format("2006-01-02 15:04:05"),
|
|
||||||
)
|
|
||||||
if len(commentString+cs) >= 2000 {
|
|
||||||
commentStrings = append(commentStrings, commentString)
|
|
||||||
commentString = ""
|
|
||||||
}
|
|
||||||
commentString += cs
|
|
||||||
}
|
|
||||||
commentStrings = append(commentStrings, commentString)
|
|
||||||
for _, c := range channels {
|
|
||||||
if c.Admin {
|
|
||||||
for _, s := range commentStrings {
|
|
||||||
_, _ = ctx.Session.ChannelMessageSend(c.ID, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
_, err = ctx.Send(fmt.Sprintf("%s your comment has been added.", ctx.Message.Author.Mention()))
|
|
||||||
dmChannel, err := ctx.Session.UserChannelCreate(request.Author.ID)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, _ = ctx.Session.ChannelMessageSend(dmChannel.ID,
|
|
||||||
fmt.Sprintf("%s has add a comment to request %d which you opened in the %s channel.\n```%s```\n```%s```",
|
|
||||||
discord_utils.GetDisplayName(ctx, author.ID),
|
|
||||||
request.ID,
|
|
||||||
discord_utils.GetChannelName(ctx, ctx.Channel.ID),
|
|
||||||
request.Content,
|
|
||||||
message,
|
|
||||||
))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var ViewCommand = &disgoman.Command{
|
|
||||||
Name: "view",
|
|
||||||
Aliases: nil,
|
|
||||||
Description: "View the details about a request.",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: 0,
|
|
||||||
Invoke: viewCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func viewCommandFunc(ctx disgoman.Context, args []string) {
|
|
||||||
guild, err := services.GuildService.GetOrCreateGuild(ctx.Guild.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Error getting Guild from the database", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
id, err := strconv.ParseInt(args[0], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Please include the ID of the request to view.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
request, err := services.RequestService.Request(id)
|
|
||||||
if err != nil || request.Guild.ID != guild.ID {
|
|
||||||
discord_utils.SendErrorMessage(ctx, fmt.Sprintf("%d is not a valid request in this guild", id), err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
requestor, err := services.UserService.GetOrCreateUser(ctx.Message.Author.ID)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "Sorry, there was an issue finding your user account", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if request.Author.ID != ctx.Message.Author.ID &&
|
|
||||||
!discord_utils.IsGuildMod(ctx, requestor) &&
|
|
||||||
!discord_utils.IsGuildAdmin(ctx, requestor) {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "You are not authorized to view that request", nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
comments, err := services.RequestService.RequestComments(request)
|
|
||||||
if err != nil {
|
|
||||||
discord_utils.SendErrorMessage(ctx, "There was an error getting the comments.", err)
|
|
||||||
}
|
|
||||||
var commentString string
|
|
||||||
var commentStrings []string
|
|
||||||
commentString = fmt.Sprintf("```md\n"+
|
|
||||||
"< Request ID Requested By >\n"+
|
|
||||||
"< %-11d %23s >\n"+
|
|
||||||
"%s\n\n"+
|
|
||||||
"Requested At: %s\n"+
|
|
||||||
"In: %s\n"+
|
|
||||||
"```",
|
|
||||||
request.ID,
|
|
||||||
discord_utils.GetDisplayName(ctx, request.Author.ID),
|
|
||||||
request.Content,
|
|
||||||
request.RequestedAt.Format("2006-01-02 15:04:05"),
|
|
||||||
discord_utils.GetChannelName(ctx, request.Channel.ID),
|
|
||||||
)
|
|
||||||
for _, c := range comments {
|
|
||||||
cs := fmt.Sprintf("```md\n%s\n- %s At %s\n```\n",
|
|
||||||
c.Content,
|
|
||||||
discord_utils.GetDisplayName(ctx, c.Author.ID),
|
|
||||||
c.CommentAt.Format("2006-01-02 15:04:05"),
|
|
||||||
)
|
|
||||||
if len(commentString+cs) >= 2000 {
|
|
||||||
commentStrings = append(commentStrings, commentString)
|
|
||||||
commentString = ""
|
|
||||||
}
|
|
||||||
commentString += cs
|
|
||||||
}
|
|
||||||
commentStrings = append(commentStrings, commentString)
|
|
||||||
for _, c := range commentStrings {
|
|
||||||
_, _ = ctx.Send(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,280 +0,0 @@
|
|||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/dustinpianalto/discordgo"
|
|
||||||
"github.com/dustinpianalto/disgoman"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/internal/discord_utils"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/pkg/services"
|
|
||||||
)
|
|
||||||
|
|
||||||
var PingCommand = &disgoman.Command{
|
|
||||||
Name: "ping",
|
|
||||||
Aliases: []string{" "},
|
|
||||||
Description: "Check the bot's ping",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: 0,
|
|
||||||
Invoke: pingCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func pingCommandFunc(ctx disgoman.Context, _ []string) {
|
|
||||||
timeBefore := time.Now()
|
|
||||||
msg, _ := ctx.Send("Pong!")
|
|
||||||
took := time.Now().Sub(timeBefore)
|
|
||||||
_, err := ctx.Session.ChannelMessageEdit(ctx.Message.ChannelID, msg.ID, fmt.Sprintf("Pong!\nPing Took **%s**", took.String()))
|
|
||||||
if err != nil {
|
|
||||||
ctx.CommandManager.ErrorChannel <- disgoman.CommandError{
|
|
||||||
Context: ctx,
|
|
||||||
Message: "Ping Failed",
|
|
||||||
Error: err,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var InviteCommand = &disgoman.Command{
|
|
||||||
Name: "invite",
|
|
||||||
Aliases: nil,
|
|
||||||
Description: "Get the invite link for this bot or others",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: 0,
|
|
||||||
Invoke: inviteCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func inviteCommandFunc(ctx disgoman.Context, args []string) {
|
|
||||||
var ids []string
|
|
||||||
if len(args) == 0 && len(ctx.Message.Mentions) == 0 {
|
|
||||||
ids = []string{ctx.Session.State.User.ID}
|
|
||||||
} else {
|
|
||||||
if len(ctx.Message.Mentions) > 0 {
|
|
||||||
for _, user := range ctx.Message.Mentions {
|
|
||||||
member, err := ctx.Session.GuildMember(ctx.Guild.ID, user.ID)
|
|
||||||
if err != nil {
|
|
||||||
ctx.CommandManager.ErrorChannel <- disgoman.CommandError{
|
|
||||||
Context: ctx,
|
|
||||||
Message: "Could not find member " + user.Username,
|
|
||||||
Error: err,
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ids = append(ids, member.User.ID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(args) > 0 {
|
|
||||||
for _, id := range args {
|
|
||||||
member, err := ctx.Session.GuildMember(ctx.Guild.ID, id)
|
|
||||||
if err != nil {
|
|
||||||
ctx.CommandManager.ErrorChannel <- disgoman.CommandError{
|
|
||||||
Context: ctx,
|
|
||||||
Message: "Could not find member " + id,
|
|
||||||
Error: err,
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ids = append(ids, member.User.ID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(ids) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, id := range ids {
|
|
||||||
url := fmt.Sprintf("<https://discordapp.com/oauth2/authorize?client_id=%v&scope=bot>", id)
|
|
||||||
_, err := ctx.Send(url)
|
|
||||||
if err != nil {
|
|
||||||
ctx.CommandManager.ErrorChannel <- disgoman.CommandError{
|
|
||||||
Context: ctx,
|
|
||||||
Message: "Couldn't send the invite link.",
|
|
||||||
Error: err,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var GitCommand = &disgoman.Command{
|
|
||||||
Name: "git",
|
|
||||||
Aliases: nil,
|
|
||||||
Description: "Show my github link",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: 0,
|
|
||||||
Invoke: gitCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func gitCommandFunc(ctx disgoman.Context, _ []string) {
|
|
||||||
embed := &discordgo.MessageEmbed{
|
|
||||||
Title: "Hi there, my code is on Gitea",
|
|
||||||
Color: 0,
|
|
||||||
URL: "https://gitea.wayfinderak.com/wayfinderak/geeksbot",
|
|
||||||
}
|
|
||||||
_, err := ctx.Session.ChannelMessageSendEmbed(ctx.Channel.ID, embed)
|
|
||||||
if err != nil {
|
|
||||||
ctx.CommandManager.ErrorChannel <- disgoman.CommandError{
|
|
||||||
Context: ctx,
|
|
||||||
Message: "Git failed",
|
|
||||||
Error: err,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var SayCommand = &disgoman.Command{
|
|
||||||
Name: "say",
|
|
||||||
Aliases: nil,
|
|
||||||
Description: "Repeat a message",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: 0,
|
|
||||||
SanitizeEveryone: true,
|
|
||||||
Invoke: sayCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func sayCommandFunc(ctx disgoman.Context, args []string) {
|
|
||||||
resp := strings.Join(args, " ")
|
|
||||||
resp = strings.ReplaceAll(resp, "@everyone", "@\ufff0everyone")
|
|
||||||
resp = strings.ReplaceAll(resp, "@here", "@\ufff0here")
|
|
||||||
_, err := ctx.Session.ChannelMessageSend(ctx.Message.ChannelID, resp)
|
|
||||||
if err != nil {
|
|
||||||
ctx.CommandManager.ErrorChannel <- disgoman.CommandError{
|
|
||||||
Context: ctx,
|
|
||||||
Message: "Say Failed",
|
|
||||||
Error: err,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var UserCommand = &disgoman.Command{
|
|
||||||
Name: "user",
|
|
||||||
Aliases: nil,
|
|
||||||
Description: "Get user info",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: 0,
|
|
||||||
Invoke: userCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func userCommandFunc(ctx disgoman.Context, args []string) {
|
|
||||||
var member *discordgo.Member
|
|
||||||
if len(args) == 0 {
|
|
||||||
member, _ = ctx.Session.GuildMember(ctx.Guild.ID, ctx.Message.Author.ID)
|
|
||||||
} else {
|
|
||||||
var err error
|
|
||||||
if len(ctx.Message.Mentions) > 0 {
|
|
||||||
member, err = ctx.Session.GuildMember(ctx.Guild.ID, ctx.Message.Mentions[0].ID)
|
|
||||||
} else {
|
|
||||||
member, err = ctx.Session.GuildMember(ctx.Guild.ID, args[0])
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
ctx.CommandManager.ErrorChannel <- disgoman.CommandError{
|
|
||||||
Context: ctx,
|
|
||||||
Message: "Couldn't get that member",
|
|
||||||
Error: err,
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
thumb := &discordgo.MessageEmbedThumbnail{
|
|
||||||
URL: member.User.AvatarURL(""),
|
|
||||||
}
|
|
||||||
|
|
||||||
var botString string
|
|
||||||
if member.User.Bot {
|
|
||||||
botString = "BOT"
|
|
||||||
} else {
|
|
||||||
botString = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
var roles []*discordgo.Role
|
|
||||||
for _, roleID := range member.Roles {
|
|
||||||
role, _ := ctx.Session.State.Role(ctx.Guild.ID, roleID)
|
|
||||||
roles = append(roles, role)
|
|
||||||
}
|
|
||||||
sort.Slice(roles, func(i, j int) bool { return roles[i].Position > roles[j].Position })
|
|
||||||
var roleMentions []string
|
|
||||||
for _, role := range roles {
|
|
||||||
roleMentions = append(roleMentions, role.Mention())
|
|
||||||
}
|
|
||||||
var rolesString string
|
|
||||||
if len(roleMentions) > 0 {
|
|
||||||
rolesString = strings.Join(roleMentions, " ")
|
|
||||||
} else {
|
|
||||||
rolesString = "None"
|
|
||||||
}
|
|
||||||
|
|
||||||
rolesField := &discordgo.MessageEmbedField{
|
|
||||||
Name: "Roles:",
|
|
||||||
Value: rolesString,
|
|
||||||
Inline: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
guildJoinedField := &discordgo.MessageEmbedField{
|
|
||||||
Name: "Joined Guild:",
|
|
||||||
Value: discord_utils.ParseDateString(member.JoinedAt),
|
|
||||||
Inline: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
int64ID, _ := strconv.ParseInt(member.User.ID, 10, 64)
|
|
||||||
s := discord_utils.ParseSnowflake(int64ID)
|
|
||||||
discordJoinedField := &discordgo.MessageEmbedField{
|
|
||||||
Name: "Joined Discord:",
|
|
||||||
Value: discord_utils.ParseDateString(s.CreationTime),
|
|
||||||
Inline: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
embed := &discordgo.MessageEmbed{
|
|
||||||
Title: fmt.Sprintf("%v#%v %v", member.User.Username, member.User.Discriminator, botString),
|
|
||||||
Description: fmt.Sprintf("**%v** (%v)", member.Nick, member.User.ID),
|
|
||||||
Color: ctx.Session.State.UserColor(member.User.ID, ctx.Channel.ID),
|
|
||||||
Thumbnail: thumb,
|
|
||||||
Fields: []*discordgo.MessageEmbedField{
|
|
||||||
guildJoinedField,
|
|
||||||
discordJoinedField,
|
|
||||||
rolesField,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
_, err := ctx.Session.ChannelMessageSendEmbed(ctx.Channel.ID, embed)
|
|
||||||
if err != nil {
|
|
||||||
ctx.CommandManager.ErrorChannel <- disgoman.CommandError{
|
|
||||||
Context: ctx,
|
|
||||||
Message: "Couldn't send the user embed",
|
|
||||||
Error: err,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var AddUserCommand = &disgoman.Command{
|
|
||||||
Name: "adduser",
|
|
||||||
Aliases: nil,
|
|
||||||
Description: "Get user info",
|
|
||||||
OwnerOnly: false,
|
|
||||||
Hidden: false,
|
|
||||||
RequiredPermissions: 0,
|
|
||||||
Invoke: addUserCommandFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
func addUserCommandFunc(ctx disgoman.Context, args []string) {
|
|
||||||
if ctx.Message.Author.ID == ctx.CommandManager.Owners[0] {
|
|
||||||
user := geeksbot.User{
|
|
||||||
ID: ctx.Message.Author.ID,
|
|
||||||
IsActive: true,
|
|
||||||
IsStaff: true,
|
|
||||||
IsAdmin: true,
|
|
||||||
}
|
|
||||||
user, err := services.UserService.CreateUser(user)
|
|
||||||
if err != nil {
|
|
||||||
ctx.CommandManager.ErrorChannel <- disgoman.CommandError{
|
|
||||||
Context: ctx,
|
|
||||||
Message: "Error with adding user",
|
|
||||||
Error: err,
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Session.MessageReactionAdd(ctx.Channel.ID, ctx.Message.ID, "✅")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package utils
|
|
||||||
|
|
||||||
func RemoveDuplicateStrings(s []string) []string {
|
|
||||||
keys := make(map[string]bool)
|
|
||||||
o := []string{}
|
|
||||||
|
|
||||||
for _, e := range s {
|
|
||||||
if _, v := keys[e]; !v {
|
|
||||||
keys[e] = true
|
|
||||||
o = append(o, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return o
|
|
||||||
}
|
|
||||||
|
|
||||||
func PluralizeString(s string, i int) string {
|
|
||||||
if i == 1 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
if s == "is" {
|
|
||||||
return "are"
|
|
||||||
}
|
|
||||||
return s + "s"
|
|
||||||
}
|
|
||||||
-24
@@ -1,24 +0,0 @@
|
|||||||
package geeksbot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Message struct {
|
|
||||||
ID string
|
|
||||||
CreatedAt time.Time
|
|
||||||
ModifiedAt sql.NullTime
|
|
||||||
Content string
|
|
||||||
PreviousContent []string
|
|
||||||
Channel Channel
|
|
||||||
Author User
|
|
||||||
}
|
|
||||||
|
|
||||||
type MessageService interface {
|
|
||||||
Message(id string) (Message, error)
|
|
||||||
CreateMessage(m Message) (Message, error)
|
|
||||||
DeleteMessage(m Message) error
|
|
||||||
ChannelMessages(c Channel) ([]Message, error)
|
|
||||||
UpdateMessage(m Message) (Message, error)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
@checks.no_bots()
|
||||||
|
@commands.cooldown(1,5,commands.BucketType.user)
|
||||||
|
@commands.command()
|
||||||
|
async def captcha(self, ctx, type, *, text):
|
||||||
|
type = type.lower()
|
||||||
|
if type not in "checked unchecked loading".split():
|
||||||
|
raise commands.BadArgument(f"Invalid type {type!r}. Available "
|
||||||
|
"types: `unchecked`, `loading`, `checked`")
|
||||||
|
font = ImageFont.truetype("Roboto-Regular.ttf", 14)
|
||||||
|
async with ctx.typing():
|
||||||
|
img = Image.open(f"blank-captcha-{type}.png")
|
||||||
|
img.load()
|
||||||
|
d = ImageDraw.Draw(img)
|
||||||
|
fnc = functools.partial(d.text, (53,30), text, fill=(0,0,0,255),
|
||||||
|
font=font)
|
||||||
|
await self.bot.loop.run_in_executor(None, fnc)
|
||||||
|
img.save("captcha.png")
|
||||||
|
await ctx.send(file=discord.File("captcha.png"))
|
||||||
|
os.system("rm captcha.png")
|
||||||
|
img.close()
|
||||||
|
|
||||||
|
|
||||||
|
import functools, youtube_dl
|
||||||
|
#bot.voice_chan = await ctx.author.voice.channel.connect()
|
||||||
|
bot.voice_chan.stop()
|
||||||
|
opts = {"format": 'webm[abr>0]/bestaudio/best',"ignoreerrors": True,"default_search": "auto","source_address": "0.0.0.0",'quiet': True}
|
||||||
|
ydl = youtube_dl.YoutubeDL(opts)
|
||||||
|
url = 'https://www.youtube.com/watch?v=hjbPszSt5Pc'
|
||||||
|
func = functools.partial(ydl.extract_info, url, download=False)
|
||||||
|
info = func()
|
||||||
|
#bot.voice_chan.play(discord.FFmpegPCMAudio('dead_puppies.mp3'))
|
||||||
|
bot.voice_chan.play(discord.FFmpegPCMAudio(info['url']))
|
||||||
|
#async while bot.voice_chan.is_playing():
|
||||||
|
# pass
|
||||||
|
#await bot.voice_chan.disconnect()
|
||||||
-34
@@ -1,34 +0,0 @@
|
|||||||
package geeksbot
|
|
||||||
|
|
||||||
import "database/sql"
|
|
||||||
|
|
||||||
type PatreonCreator struct {
|
|
||||||
ID int
|
|
||||||
Creator string
|
|
||||||
Link string
|
|
||||||
Guild Guild
|
|
||||||
}
|
|
||||||
|
|
||||||
type PatreonTier struct {
|
|
||||||
ID int
|
|
||||||
Name string
|
|
||||||
Description sql.NullString
|
|
||||||
Creator PatreonCreator
|
|
||||||
Role Role
|
|
||||||
NextTier *PatreonTier
|
|
||||||
}
|
|
||||||
|
|
||||||
type PatreonService interface {
|
|
||||||
PatreonCreatorByID(id int) (PatreonCreator, error)
|
|
||||||
PatreonCreatorByName(name string, guild Guild) (PatreonCreator, error)
|
|
||||||
CreatePatreonCreator(c PatreonCreator) (PatreonCreator, error)
|
|
||||||
UpdatePatreonCreator(c PatreonCreator) (PatreonCreator, error)
|
|
||||||
DeletePatreonCreator(c PatreonCreator) error
|
|
||||||
PatreonTierByID(id int) (PatreonTier, error)
|
|
||||||
PatreonTierByName(name string, creator string) (PatreonTier, error)
|
|
||||||
CreatePatreonTier(t PatreonTier) (PatreonTier, error)
|
|
||||||
UpdatePatreonTier(t PatreonTier) (PatreonTier, error)
|
|
||||||
DeletePatreonTier(t PatreonTier) error
|
|
||||||
GuildPatreonCreators(g Guild) ([]PatreonCreator, error)
|
|
||||||
CreatorPatreonTiers(c PatreonCreator) ([]PatreonTier, error)
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
package database
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot"
|
|
||||||
)
|
|
||||||
|
|
||||||
type channelService struct {
|
|
||||||
db *sql.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s channelService) Channel(id string) (geeksbot.Channel, error) {
|
|
||||||
var channel geeksbot.Channel
|
|
||||||
var guild_id string
|
|
||||||
queryString := "SELECT id, guild_id, admin, default_channel, new_patron FROM channels WHERE id = $1"
|
|
||||||
row := s.db.QueryRow(queryString, id)
|
|
||||||
err := row.Scan(&channel.ID, &guild_id, &channel.Admin, &channel.Default, &channel.NewPatron)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Channel{}, err
|
|
||||||
}
|
|
||||||
guild, err := GuildService.Guild(guild_id)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Channel{}, err
|
|
||||||
}
|
|
||||||
channel.Guild = guild
|
|
||||||
return channel, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s channelService) CreateChannel(c geeksbot.Channel) (geeksbot.Channel, error) {
|
|
||||||
queryString := "INSERT INTO channels (id, guild_id, admin, default_channel, new_patron) VALUES ($1, $2, $3, $4, $5)"
|
|
||||||
_, err := s.db.Exec(queryString, c.ID, c.Guild.ID, c.Admin, c.Default, c.NewPatron)
|
|
||||||
return c, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s channelService) DeleteChannel(c geeksbot.Channel) error {
|
|
||||||
queryString := "DELETE FROM channels WHERE id = $1"
|
|
||||||
_, err := s.db.Exec(queryString, c.ID)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s channelService) UpdateChannel(c geeksbot.Channel) (geeksbot.Channel, error) {
|
|
||||||
queryString := "UPDATE channels SET admin = $2, default_channel = $3, new_patron = $4 WHERE id = $1"
|
|
||||||
_, err := s.db.Exec(queryString, c.ID, c.Admin, c.Default, c.NewPatron)
|
|
||||||
return c, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s channelService) GuildChannels(g geeksbot.Guild) ([]geeksbot.Channel, error) {
|
|
||||||
var channels []geeksbot.Channel
|
|
||||||
queryString := "SELECT id FROM channels WHERE guild_id = $1"
|
|
||||||
rows, err := s.db.Query(queryString, g.ID)
|
|
||||||
if err != nil {
|
|
||||||
return channels, err
|
|
||||||
}
|
|
||||||
for rows.Next() {
|
|
||||||
var id string
|
|
||||||
err = rows.Scan(&id)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
channel, err := s.Channel(id)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
channels = append(channels, channel)
|
|
||||||
}
|
|
||||||
return channels, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s channelService) GetOrCreateChannel(id string, guild_id string) (geeksbot.Channel, error) {
|
|
||||||
channel, err := s.Channel(id)
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
var guild geeksbot.Guild
|
|
||||||
guild, err = GuildService.GetOrCreateGuild(guild_id)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Channel{}, err
|
|
||||||
}
|
|
||||||
channel, err = s.CreateChannel(geeksbot.Channel{
|
|
||||||
ID: id,
|
|
||||||
Guild: guild,
|
|
||||||
Admin: false,
|
|
||||||
Default: false,
|
|
||||||
NewPatron: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return channel, err
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
package database
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/pkg/database/migrations"
|
|
||||||
"github.com/golang-migrate/migrate/v4"
|
|
||||||
"github.com/golang-migrate/migrate/v4/database/postgres"
|
|
||||||
bindata "github.com/golang-migrate/migrate/v4/source/go_bindata"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
db *sql.DB
|
|
||||||
GuildService guildService
|
|
||||||
UserService userService
|
|
||||||
ChannelService channelService
|
|
||||||
MessageService messageService
|
|
||||||
RequestService requestService
|
|
||||||
PatreonService patreonService
|
|
||||||
ServerService serverService
|
|
||||||
)
|
|
||||||
|
|
||||||
func ConnectDatabase(dbConnString string) {
|
|
||||||
var err error
|
|
||||||
db, err = sql.Open("postgres", dbConnString)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(fmt.Errorf("can't connect to the database: %w", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
db.SetMaxOpenConns(5)
|
|
||||||
db.SetMaxIdleConns(2)
|
|
||||||
db.SetConnMaxLifetime(30 * time.Minute)
|
|
||||||
db.SetConnMaxIdleTime(5 * time.Minute)
|
|
||||||
|
|
||||||
if err = db.Ping(); err != nil {
|
|
||||||
log.Fatal(fmt.Errorf("can't ping the database: %w", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Database Connected.")
|
|
||||||
initServices()
|
|
||||||
log.Println("Services Initialized")
|
|
||||||
}
|
|
||||||
|
|
||||||
func RunMigrations() {
|
|
||||||
s := bindata.Resource(migrations.AssetNames(),
|
|
||||||
func(name string) ([]byte, error) {
|
|
||||||
return migrations.Asset(name)
|
|
||||||
})
|
|
||||||
d, err := bindata.WithInstance(s)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(fmt.Errorf("cannot load migrations: %w", err))
|
|
||||||
}
|
|
||||||
instance, err := postgres.WithInstance(db, &postgres.Config{})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(fmt.Errorf("cannot create db driver: %w", err))
|
|
||||||
}
|
|
||||||
m, err := migrate.NewWithInstance("go-bindatafoo", d, "postgres", instance)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(fmt.Errorf("cannot create migration instance: %w", err))
|
|
||||||
}
|
|
||||||
err = m.Up()
|
|
||||||
if err != nil {
|
|
||||||
if err.Error() == "no change" {
|
|
||||||
log.Println(err)
|
|
||||||
} else {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Println("Migrations Run")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func initServices() {
|
|
||||||
GuildService = guildService{db: db}
|
|
||||||
UserService = userService{db: db}
|
|
||||||
ChannelService = channelService{db: db}
|
|
||||||
MessageService = messageService{db: db}
|
|
||||||
PatreonService = patreonService{db: db}
|
|
||||||
RequestService = requestService{db: db}
|
|
||||||
ServerService = serverService{db: db}
|
|
||||||
}
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
package database
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot"
|
|
||||||
"github.com/lib/pq"
|
|
||||||
)
|
|
||||||
|
|
||||||
type guildService struct {
|
|
||||||
db *sql.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s guildService) Guild(id string) (geeksbot.Guild, error) {
|
|
||||||
var g geeksbot.Guild
|
|
||||||
queryString := "SELECT id, new_patron_message, prefixes FROM guilds WHERE id = $1"
|
|
||||||
row := s.db.QueryRow(queryString, id)
|
|
||||||
err := row.Scan(&g.ID, &g.NewPatronMessage, pq.Array(&g.Prefixes))
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Guild{}, err
|
|
||||||
}
|
|
||||||
return g, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s guildService) CreateGuild(g geeksbot.Guild) (geeksbot.Guild, error) {
|
|
||||||
queryString := "INSERT INTO guilds (id, new_patron_message, prefixes) VALUES ($1, $2, $3)"
|
|
||||||
_, err := s.db.Exec(queryString, g.ID, g.NewPatronMessage, pq.Array(g.Prefixes))
|
|
||||||
return g, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s guildService) DeleteGuild(g geeksbot.Guild) error {
|
|
||||||
queryString := "DELETE FROM guilds WHERE id = $1"
|
|
||||||
_, err := s.db.Exec(queryString, g.ID)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s guildService) UpdateGuild(g geeksbot.Guild) (geeksbot.Guild, error) {
|
|
||||||
queryString := "UPDATE guilds SET new_patron_message = $2, prefixes = $3 WHERE id = $1"
|
|
||||||
_, err := s.db.Exec(queryString, g.ID, g.NewPatronMessage, pq.Array(g.Prefixes))
|
|
||||||
return g, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s guildService) GuildRoles(g geeksbot.Guild) ([]geeksbot.Role, error) {
|
|
||||||
var roles []geeksbot.Role
|
|
||||||
queryString := "SELECT id FROM roles WHERE guild_id = $1"
|
|
||||||
rows, err := s.db.Query(queryString, g.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for rows.Next() {
|
|
||||||
var id string
|
|
||||||
err = rows.Scan(&id)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
role, err := s.Role(id)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
roles = append(roles, role)
|
|
||||||
}
|
|
||||||
return roles, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s guildService) CreateRole(r geeksbot.Role) (geeksbot.Role, error) {
|
|
||||||
queryString := "INSERT INTO roles (id, role_type, guild_id) VALUES ($1, $2, $3)"
|
|
||||||
_, err := s.db.Exec(queryString, r.ID, r.RoleType, r.Guild.ID)
|
|
||||||
return r, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s guildService) Role(id string) (geeksbot.Role, error) {
|
|
||||||
var role geeksbot.Role
|
|
||||||
var guild_id string
|
|
||||||
queryString := "SELECT id, role_type, guild_id FROM roles WHERE id = $1"
|
|
||||||
row := s.db.QueryRow(queryString, id)
|
|
||||||
err := row.Scan(&role.ID, &role.RoleType, &guild_id)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Role{}, err
|
|
||||||
}
|
|
||||||
guild, err := s.Guild(guild_id)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Role{}, err
|
|
||||||
}
|
|
||||||
role.Guild = guild
|
|
||||||
return role, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s guildService) UpdateRole(r geeksbot.Role) (geeksbot.Role, error) {
|
|
||||||
queryString := "UPDATE roles SET role_type = $2 WHERE id = $1"
|
|
||||||
_, err := s.db.Exec(queryString, r.ID, r.RoleType)
|
|
||||||
return r, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s guildService) DeleteRole(r geeksbot.Role) error {
|
|
||||||
queryString := "DELETE FROM roles WHERE id = $1"
|
|
||||||
_, err := s.db.Exec(queryString, r.ID)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s guildService) GetOrCreateGuild(id string) (geeksbot.Guild, error) {
|
|
||||||
guild, err := s.Guild(id)
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
guild = geeksbot.Guild{
|
|
||||||
ID: id,
|
|
||||||
Prefixes: []string{},
|
|
||||||
}
|
|
||||||
guild, err = s.CreateGuild(guild)
|
|
||||||
}
|
|
||||||
return guild, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s guildService) CreateOrUpdateRole(r geeksbot.Role) (geeksbot.Role, error) {
|
|
||||||
role, err := s.CreateRole(r)
|
|
||||||
if err != nil && err.Error() == `pq: duplicate key value violates unique constraint "roles_pkey"` {
|
|
||||||
role, err = s.UpdateRole(r)
|
|
||||||
}
|
|
||||||
return role, err
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
package database
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot"
|
|
||||||
"github.com/lib/pq"
|
|
||||||
)
|
|
||||||
|
|
||||||
type messageService struct {
|
|
||||||
db *sql.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s messageService) Message(id string) (geeksbot.Message, error) {
|
|
||||||
var m geeksbot.Message
|
|
||||||
var channel_id string
|
|
||||||
var author_id string
|
|
||||||
queryString := `SELECT m.id, m.created_at, m.modified_at, m.content,
|
|
||||||
m.previous_content, m.channel_id, m.author_id
|
|
||||||
FROM messages as m
|
|
||||||
WHERE m.id = $1`
|
|
||||||
row := s.db.QueryRow(queryString, id)
|
|
||||||
err := row.Scan(&m.ID, &m.CreatedAt, &m.ModifiedAt, &m.Content,
|
|
||||||
pq.Array(&m.PreviousContent), &channel_id, &author_id)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Message{}, err
|
|
||||||
}
|
|
||||||
author, err := UserService.User(author_id)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Message{}, err
|
|
||||||
}
|
|
||||||
m.Author = author
|
|
||||||
channel, err := ChannelService.Channel(channel_id)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Message{}, err
|
|
||||||
}
|
|
||||||
m.Channel = channel
|
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s messageService) CreateMessage(m geeksbot.Message) (geeksbot.Message, error) {
|
|
||||||
queryString := `INSERT INTO messages (id, created_at, content, channel_id, author_id)
|
|
||||||
VALUES ($1, $2, $3, $4, $5)`
|
|
||||||
_, err := s.db.Exec(queryString, m.ID, m.CreatedAt, m.Content, m.Channel.ID, m.Author.ID)
|
|
||||||
return m, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s messageService) UpdateMessage(m geeksbot.Message) (geeksbot.Message, error) {
|
|
||||||
var content string
|
|
||||||
var previousContent []string
|
|
||||||
queryString := "SELECT content, previous_content FROM messages WHERE id = $1"
|
|
||||||
row := s.db.QueryRow(queryString, m.ID)
|
|
||||||
err := row.Scan(&content, &previousContent)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Message{}, err
|
|
||||||
}
|
|
||||||
if m.Content != content {
|
|
||||||
previousContent = append(previousContent, content)
|
|
||||||
}
|
|
||||||
queryString = "UPDATE messages SET modified_at = $2, content = $3, previous_content = $4 WHERE id = $1"
|
|
||||||
_, err = s.db.Exec(queryString, m.ID, m.ModifiedAt, m.Content, previousContent)
|
|
||||||
m.PreviousContent = previousContent
|
|
||||||
return m, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s messageService) DeleteMessage(m geeksbot.Message) error {
|
|
||||||
queryString := "DELETE FROM messages WHERE id = $1"
|
|
||||||
_, err := s.db.Exec(queryString, m.ID)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s messageService) ChannelMessages(c geeksbot.Channel) ([]geeksbot.Message, error) {
|
|
||||||
var messages []geeksbot.Message
|
|
||||||
queryString := `SELECT id FROM messages WHERE channel_id = $1`
|
|
||||||
rows, err := s.db.Query(queryString, c.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for rows.Next() {
|
|
||||||
var id string
|
|
||||||
err = rows.Scan(&id)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
m, err := s.Message(id)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
messages = append(messages, m)
|
|
||||||
}
|
|
||||||
return messages, nil
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
DROP TABLE IF EXISTS servers;
|
|
||||||
DROP TABLE IF EXISTS comments;
|
|
||||||
DROP TABLE IF EXISTS requests;
|
|
||||||
DROP TABLE IF EXISTS patreon_tier;
|
|
||||||
DROP TABLE IF EXISTS patreon_creator;
|
|
||||||
DROP TABLE IF EXISTS messages;
|
|
||||||
DROP TABLE IF EXISTS users;
|
|
||||||
DROP TABLE IF EXISTS channels;
|
|
||||||
DROP TABLE IF EXISTS roles;
|
|
||||||
DROP TYPE IF EXISTS role_types;
|
|
||||||
DROP TABLE IF EXISTS guilds;
|
|
||||||
COMMIT;
|
|
||||||
@@ -1,174 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
CREATE TABLE IF NOT EXISTS guilds (
|
|
||||||
id varchar(30),
|
|
||||||
new_patron_message varchar(1000),
|
|
||||||
prefixes varchar(10)[],
|
|
||||||
PRIMARY KEY(id)
|
|
||||||
);
|
|
||||||
CREATE TYPE role_type as ENUM (
|
|
||||||
'normal',
|
|
||||||
'moderator',
|
|
||||||
'admin',
|
|
||||||
'patreon'
|
|
||||||
);
|
|
||||||
CREATE TABLE IF NOT EXISTS roles (
|
|
||||||
id varchar(30),
|
|
||||||
role_type role_type,
|
|
||||||
guild_id varchar(30),
|
|
||||||
PRIMARY KEY(id),
|
|
||||||
CONSTRAINT fk_guild
|
|
||||||
FOREIGN KEY(guild_id)
|
|
||||||
REFERENCES guilds(id)
|
|
||||||
ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
CREATE TABLE IF NOT EXISTS channels (
|
|
||||||
id varchar(30),
|
|
||||||
guild_id varchar(30),
|
|
||||||
admin boolean,
|
|
||||||
default_channel boolean,
|
|
||||||
new_patron boolean,
|
|
||||||
PRIMARY KEY(id),
|
|
||||||
CONSTRAINT fk_guild
|
|
||||||
FOREIGN KEY(guild_id)
|
|
||||||
REFERENCES guilds(id)
|
|
||||||
ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
|
||||||
id varchar(30),
|
|
||||||
steam_id varchar(30),
|
|
||||||
active boolean,
|
|
||||||
staff boolean,
|
|
||||||
admin boolean,
|
|
||||||
PRIMARY KEY(id)
|
|
||||||
);
|
|
||||||
CREATE TABLE IF NOT EXISTS messages (
|
|
||||||
id varchar(30),
|
|
||||||
created_at timestamp,
|
|
||||||
modified_at timestamp,
|
|
||||||
content varchar(2000),
|
|
||||||
previous_content varchar(2000)[],
|
|
||||||
channel_id varchar(30),
|
|
||||||
author_id varchar(30),
|
|
||||||
embed json,
|
|
||||||
previous_embeds json[],
|
|
||||||
PRIMARY KEY(id),
|
|
||||||
CONSTRAINT fk_channel
|
|
||||||
FOREIGN KEY(channel_id)
|
|
||||||
REFERENCES channels(id)
|
|
||||||
ON DELETE CASCADE,
|
|
||||||
CONSTRAINT fk_user
|
|
||||||
FOREIGN KEY(author_id)
|
|
||||||
REFERENCES users(id)
|
|
||||||
ON DELETE SET NULL
|
|
||||||
);
|
|
||||||
CREATE TABLE IF NOT EXISTS patreon_creator (
|
|
||||||
id integer GENERATED ALWAYS AS IDENTITY,
|
|
||||||
creator varchar(100),
|
|
||||||
link varchar(200),
|
|
||||||
guild_id varchar(30),
|
|
||||||
PRIMARY KEY(id),
|
|
||||||
CONSTRAINT fk_guild
|
|
||||||
FOREIGN KEY(guild_id)
|
|
||||||
REFERENCES guilds(id)
|
|
||||||
ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
CREATE TABLE IF NOT EXISTS patreon_tier (
|
|
||||||
id integer GENERATED ALWAYS AS IDENTITY,
|
|
||||||
name varchar(100),
|
|
||||||
description varchar(1000),
|
|
||||||
creator integer,
|
|
||||||
role varchar(30),
|
|
||||||
next_tier integer,
|
|
||||||
PRIMARY KEY(id),
|
|
||||||
CONSTRAINT fk_creator
|
|
||||||
FOREIGN KEY(creator)
|
|
||||||
REFERENCES patreon_creator(id)
|
|
||||||
ON DELETE CASCADE,
|
|
||||||
CONSTRAINT fk_role
|
|
||||||
FOREIGN KEY(role)
|
|
||||||
REFERENCES roles(id)
|
|
||||||
ON DELETE SET NULL,
|
|
||||||
CONSTRAINT fk_tier
|
|
||||||
FOREIGN KEY(next_tier)
|
|
||||||
REFERENCES patreon_tier(id)
|
|
||||||
ON DELETE SET NULL
|
|
||||||
);
|
|
||||||
CREATE TABLE IF NOT EXISTS requests (
|
|
||||||
id integer GENERATED ALWAYS AS IDENTITY,
|
|
||||||
author_id varchar(30),
|
|
||||||
channel_id varchar(30),
|
|
||||||
content varchar(1000),
|
|
||||||
requested_at timestamp,
|
|
||||||
completed_at timestamp,
|
|
||||||
completed boolean,
|
|
||||||
completed_by varchar(30),
|
|
||||||
message_id varchar(30),
|
|
||||||
completed_message varchar(1000),
|
|
||||||
PRIMARY KEY(id),
|
|
||||||
CONSTRAINT fk_user
|
|
||||||
FOREIGN KEY(author_id)
|
|
||||||
REFERENCES users(id)
|
|
||||||
ON DELETE CASCADE,
|
|
||||||
CONSTRAINT fk_channel
|
|
||||||
FOREIGN KEY(channel_id)
|
|
||||||
REFERENCES channels(id)
|
|
||||||
ON DELETE CASCADE,
|
|
||||||
CONSTRAINT fk_completed_by
|
|
||||||
FOREIGN KEY(completed_by)
|
|
||||||
REFERENCES users(id)
|
|
||||||
ON DELETE SET NULL,
|
|
||||||
CONSTRAINT fk_message
|
|
||||||
FOREIGN KEY(message_id)
|
|
||||||
REFERENCES messages(id)
|
|
||||||
ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
CREATE TABLE IF NOT EXISTS comments (
|
|
||||||
id integer GENERATED ALWAYS AS IDENTITY,
|
|
||||||
author_id varchar(30),
|
|
||||||
request_id integer,
|
|
||||||
comment_at timestamp,
|
|
||||||
content varchar(1000),
|
|
||||||
PRIMARY KEY(id),
|
|
||||||
CONSTRAINT fk_user
|
|
||||||
FOREIGN KEY(author_id)
|
|
||||||
REFERENCES users(id)
|
|
||||||
ON DELETE CASCADE,
|
|
||||||
CONSTRAINT fk_request
|
|
||||||
FOREIGN KEY(request_id)
|
|
||||||
REFERENCES requests(id)
|
|
||||||
ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
CREATE TABLE IF NOT EXISTS servers (
|
|
||||||
id integer GENERATED ALWAYS AS IDENTITY,
|
|
||||||
name varchar(100),
|
|
||||||
ip_address varchar(15),
|
|
||||||
port integer,
|
|
||||||
password varchar(200),
|
|
||||||
alerts_channel_id varchar(30),
|
|
||||||
guild_id varchar(30),
|
|
||||||
info_channel_id varchar(30),
|
|
||||||
info_message_id varchar(30),
|
|
||||||
settings_message_id varchar(30),
|
|
||||||
PRIMARY KEY(id),
|
|
||||||
CONSTRAINT fk_alert_channel
|
|
||||||
FOREIGN KEY(alerts_channel_id)
|
|
||||||
REFERENCES channels(id)
|
|
||||||
ON DELETE SET NULL,
|
|
||||||
CONSTRAINT fk_guild
|
|
||||||
FOREIGN KEY(guild_id)
|
|
||||||
REFERENCES guilds(id)
|
|
||||||
ON DELETE CASCADE,
|
|
||||||
CONSTRAINT fk_info_channel
|
|
||||||
FOREIGN KEY(info_channel_id)
|
|
||||||
REFERENCES channels(id)
|
|
||||||
ON DELETE SET NULL,
|
|
||||||
CONSTRAINT fk_info_message
|
|
||||||
FOREIGN KEY(info_message_id)
|
|
||||||
REFERENCES messages(id)
|
|
||||||
ON DELETE SET NULL,
|
|
||||||
CONSTRAINT fk_settings_message
|
|
||||||
FOREIGN KEY(settings_message_id)
|
|
||||||
REFERENCES messages(id)
|
|
||||||
ON DELETE SET NULL
|
|
||||||
);
|
|
||||||
COMMIT;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ALTER TABLE requests DROP COLUMN guild_id;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE requests
|
|
||||||
ADD COLUMN guild_id varchar(30) CONSTRAINT fk_guild REFERENCES guilds(id) ON DELETE CASCADE;
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
ALTER TABLE users
|
|
||||||
ALTER COLUMN active DROP NOT NULL,
|
|
||||||
ALTER COLUMN staff DROP NOT NULL,
|
|
||||||
ALTER COLUMN admin DROP NOT NULL;
|
|
||||||
ALTER TABLE channels
|
|
||||||
ALTER COLUMN guild_id DROP NOT NULL,
|
|
||||||
ALTER COLUMN admin DROP NOT NULL,
|
|
||||||
ALTER COLUMN default_channel DROP NOT NULL,
|
|
||||||
ALTER COLUMN new_patron DROP NOT NULL;
|
|
||||||
ALTER TABLE messages
|
|
||||||
ALTER COLUMN created_at DROP NOT NULL,
|
|
||||||
ALTER COLUMN content DROP NOT NULL,
|
|
||||||
ALTER COLUMN channel_id DROP NOT NULL,
|
|
||||||
ALTER COLUMN author_id DROP NOT NULL;
|
|
||||||
ALTER TABLE messages
|
|
||||||
ADD COLUMN embed json,
|
|
||||||
ADD COLUMN previous_embeds json[];
|
|
||||||
ALTER TABLE patreon_creators
|
|
||||||
ALTER COLUMN creator DROP NOT NULL,
|
|
||||||
ALTER COLUMN link DROP NOT NULL,
|
|
||||||
ALTER COLUMN guild_id DROP NOT NULL;
|
|
||||||
ALTER TABLE patreon_creators
|
|
||||||
RENAME TO patreon_creator;
|
|
||||||
ALTER TABLE patreon_tiers
|
|
||||||
ALTER COLUMN name DROP NOT NULL,
|
|
||||||
ALTER COLUMN creator DROP NOT NULL,
|
|
||||||
ALTER COLUMN role DROP NOT NULL;
|
|
||||||
ALTER TABLE patreon_tiers
|
|
||||||
RENAME TO patreon_tier;
|
|
||||||
ALTER TABLE requests
|
|
||||||
ALTER COLUMN author_id DROP NOT NULL,
|
|
||||||
ALTER COLUMN channel_id DROP NOT NULL,
|
|
||||||
ALTER COLUMN content DROP NOT NULL,
|
|
||||||
ALTER COLUMN requested_at DROP NOT NULL,
|
|
||||||
ALTER COLUMN completed DROP NOT NULL,
|
|
||||||
ALTER COLUMN message_id DROP NOT NULL,
|
|
||||||
ALTER COLUMN guild_id DROP NOT NULL;
|
|
||||||
ALTER TABLE roles
|
|
||||||
ALTER COLUMN role_type DROP NOT NULL,
|
|
||||||
ALTER COLUMN guild_id DROP NOT NULL;
|
|
||||||
ALTER TABLE servers
|
|
||||||
ALTER COLUMN name DROP NOT NULL,
|
|
||||||
ALTER COLUMN ip_address DROP NOT NULL,
|
|
||||||
ALTER COLUMN port DROP NOT NULL,
|
|
||||||
ALTER COLUMN password DROP NOT NULL,
|
|
||||||
ALTER COLUMN guild_id DROP NOT NULL;
|
|
||||||
COMMIT;
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
ALTER TABLE users
|
|
||||||
ALTER COLUMN active SET NOT NULL,
|
|
||||||
ALTER COLUMN staff SET NOT NULL,
|
|
||||||
ALTER COLUMN admin SET NOT NULL;
|
|
||||||
ALTER TABLE channels
|
|
||||||
ALTER COLUMN guild_id SET NOT NULL,
|
|
||||||
ALTER COLUMN admin SET NOT NULL,
|
|
||||||
ALTER COLUMN default_channel SET NOT NULL,
|
|
||||||
ALTER COLUMN new_patron SET NOT NULL;
|
|
||||||
ALTER TABLE messages
|
|
||||||
ALTER COLUMN created_at SET NOT NULL,
|
|
||||||
ALTER COLUMN content SET NOT NULL,
|
|
||||||
ALTER COLUMN channel_id SET NOT NULL,
|
|
||||||
ALTER COLUMN author_id SET NOT NULL;
|
|
||||||
ALTER TABLE messages
|
|
||||||
DROP COLUMN embed,
|
|
||||||
DROP COLUMN previous_embeds;
|
|
||||||
ALTER TABLE patreon_creator
|
|
||||||
ALTER COLUMN creator SET NOT NULL,
|
|
||||||
ALTER COLUMN link SET NOT NULL,
|
|
||||||
ALTER COLUMN guild_id SET NOT NULL;
|
|
||||||
ALTER TABLE patreon_creator
|
|
||||||
RENAME TO patreon_creators;
|
|
||||||
ALTER TABLE patreon_tier
|
|
||||||
ALTER COLUMN name SET NOT NULL,
|
|
||||||
ALTER COLUMN creator SET NOT NULL,
|
|
||||||
ALTER COLUMN role SET NOT NULL;
|
|
||||||
ALTER TABLE patreon_tier
|
|
||||||
RENAME TO patreon_tiers;
|
|
||||||
ALTER TABLE requests
|
|
||||||
ALTER COLUMN author_id SET NOT NULL,
|
|
||||||
ALTER COLUMN channel_id SET NOT NULL,
|
|
||||||
ALTER COLUMN content SET NOT NULL,
|
|
||||||
ALTER COLUMN requested_at SET NOT NULL,
|
|
||||||
ALTER COLUMN completed SET NOT NULL,
|
|
||||||
ALTER COLUMN message_id SET NOT NULL,
|
|
||||||
ALTER COLUMN guild_id SET NOT NULL;
|
|
||||||
ALTER TABLE roles
|
|
||||||
ALTER COLUMN role_type SET NOT NULL,
|
|
||||||
ALTER COLUMN guild_id SET NOT NULL;
|
|
||||||
ALTER TABLE servers
|
|
||||||
ALTER COLUMN name SET NOT NULL,
|
|
||||||
ALTER COLUMN ip_address SET NOT NULL,
|
|
||||||
ALTER COLUMN port SET NOT NULL,
|
|
||||||
ALTER COLUMN password SET NOT NULL,
|
|
||||||
ALTER COLUMN guild_id SET NOT NULL;
|
|
||||||
COMMIT;
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
CREATE TYPE role_type_new AS ENUM (
|
|
||||||
'normal',
|
|
||||||
'moderator',
|
|
||||||
'admin',
|
|
||||||
'patreon',
|
|
||||||
);
|
|
||||||
UPDATE roles SET role_type = 'normal' WHERE role_type = 'sar';
|
|
||||||
ALTER TABLE roles
|
|
||||||
ALTER COLUMN roles TYPE role_type_new;
|
|
||||||
USING (roles::text::role_type_new)
|
|
||||||
DROP TYPE role_type;
|
|
||||||
ALTER TYPE role_type_new RENAME TO role_type;
|
|
||||||
COMMIT;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ALTER TYPE role_type ADD VALUE 'sar';
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
ALTER TABLE messages
|
|
||||||
ALTER COLUMN previous_content DROP NOT NULL;
|
|
||||||
ALTER TABLE messages
|
|
||||||
ALTER COLUMN previous_content DROP DEFAULT;
|
|
||||||
COMMIT;
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
ALTER TABLE messages
|
|
||||||
ALTER COLUMN previous_content SET NOT NULL;
|
|
||||||
ALTER TABLE messages
|
|
||||||
ALTER COLUMN previous_content SET DEFAULT array[]::varchar[];
|
|
||||||
COMMIT;
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
ALTER TABLE servers
|
|
||||||
DROP COLUMN ftp_port,
|
|
||||||
DROP COLUMN ftp_username,
|
|
||||||
DROP COLUMN ftp_password;
|
|
||||||
COMMIT;
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
ALTER TABLE servers
|
|
||||||
ADD COLUMN ftp_port int4,
|
|
||||||
ADD COLUMN ftp_username varchar(200),
|
|
||||||
ADD COLUMN ftp_password varchar(200);
|
|
||||||
COMMIT;
|
|
||||||
@@ -1,267 +0,0 @@
|
|||||||
// Code generated for package migrations by go-bindata DO NOT EDIT. (@generated)
|
|
||||||
// sources:
|
|
||||||
// 000001_init_schema.down.sql
|
|
||||||
// 000001_init_schema.up.sql
|
|
||||||
package migrations
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"compress/gzip"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func bindataRead(data []byte, name string) ([]byte, error) {
|
|
||||||
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Read %q: %v", name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
_, err = io.Copy(&buf, gz)
|
|
||||||
clErr := gz.Close()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Read %q: %v", name, err)
|
|
||||||
}
|
|
||||||
if clErr != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type asset struct {
|
|
||||||
bytes []byte
|
|
||||||
info os.FileInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
type bindataFileInfo struct {
|
|
||||||
name string
|
|
||||||
size int64
|
|
||||||
mode os.FileMode
|
|
||||||
modTime time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name return file name
|
|
||||||
func (fi bindataFileInfo) Name() string {
|
|
||||||
return fi.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size return file size
|
|
||||||
func (fi bindataFileInfo) Size() int64 {
|
|
||||||
return fi.size
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mode return file mode
|
|
||||||
func (fi bindataFileInfo) Mode() os.FileMode {
|
|
||||||
return fi.mode
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mode return file modify time
|
|
||||||
func (fi bindataFileInfo) ModTime() time.Time {
|
|
||||||
return fi.modTime
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsDir return file whether a directory
|
|
||||||
func (fi bindataFileInfo) IsDir() bool {
|
|
||||||
return fi.mode&os.ModeDir != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sys return file is sys mode
|
|
||||||
func (fi bindataFileInfo) Sys() interface{} {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var __000001_init_schemaDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\xcf\x3d\x0e\xc2\x30\x0c\x86\xe1\x3d\xa7\xf0\x3d\x32\x51\x08\x28\x12\xa5\x15\xcd\x00\x53\x15\x15\xab\x54\xca\x4f\xb1\x13\x24\x6e\xcf\x0c\x83\x61\xf6\x23\x7d\x7e\x1b\x73\xb0\x27\xad\x00\x00\x76\xe7\xae\x07\xb7\x69\x8e\x06\xec\x1e\xcc\xc5\x0e\x6e\x00\x46\x7a\x22\xb1\x20\xa6\x1c\x23\xa6\x22\x11\xc2\x47\x45\x16\xc9\xea\x0b\x61\x4e\x63\x59\x90\xfe\x60\x13\xa1\x2f\x59\x92\x11\x99\xfd\x8c\xd2\x66\xe5\x1f\x65\x77\x9f\x12\x06\xb1\x2c\x87\xcf\x89\x6b\xff\x7d\x1e\xcb\x6b\x15\xdf\x98\xeb\x12\x6e\xac\xd5\xb6\x6b\x5b\xeb\xb4\x7a\x07\x00\x00\xff\xff\x57\xde\x8f\x03\x93\x01\x00\x00")
|
|
||||||
|
|
||||||
func _000001_init_schemaDownSqlBytes() ([]byte, error) {
|
|
||||||
return bindataRead(
|
|
||||||
__000001_init_schemaDownSql,
|
|
||||||
"000001_init_schema.down.sql",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _000001_init_schemaDownSql() (*asset, error) {
|
|
||||||
bytes, err := _000001_init_schemaDownSqlBytes()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
info := bindataFileInfo{name: "000001_init_schema.down.sql", size: 403, mode: os.FileMode(420), modTime: time.Unix(1611206279, 0)}
|
|
||||||
a := &asset{bytes: bytes, info: info}
|
|
||||||
return a, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var __000001_init_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xd4\x58\x5d\x6f\xa3\x38\x14\x7d\xef\xaf\xf0\x5b\x12\x69\x1f\xba\xbb\xda\xa7\x3e\xd1\xc4\xad\xd0\x26\xa4\x22\x54\xbb\xd1\x68\x84\xdc\x70\x93\x7a\x0a\x36\x63\x3b\x69\xfb\xef\x47\x50\x3e\x1c\xc0\xe0\x51\x99\x6a\xca\x53\xe4\x7b\xb1\x8f\xcf\x3d\xf7\x83\x5c\xe3\x5b\xd7\xbb\xba\x40\x08\xa1\xb9\x8f\x9d\x00\xa3\xc0\xb9\x5e\x62\xe4\xde\x20\x6f\x1d\x20\xfc\xbf\xbb\x09\x36\xe8\x70\xa4\x71\x24\xd1\x34\xf7\xcb\x1e\x1a\xa1\x13\x11\xbb\x47\x22\xa6\x7f\x5f\xce\xfe\xa8\xd6\x19\x3c\x87\x29\x51\x82\xb3\x30\x01\x29\xc9\x01\x2a\xbf\x3f\x2f\x2f\x75\xcf\x54\xc0\x9e\xbe\x80\xd4\xec\xb3\x2f\x5f\x6b\xfb\x9d\xef\xae\x1c\x7f\x8b\xfe\xc5\xdb\x29\x8d\x66\xf9\xfa\xec\x1c\xe8\xf6\x0e\x23\xc1\x63\x08\xd5\x6b\x0a\x88\x48\x84\xbd\xfb\x95\x06\x72\xc2\xb8\x48\x48\x3c\xa9\x37\x9d\x24\x3c\x02\x41\x14\x17\xfa\x22\x89\x12\xca\xf4\x85\xec\x06\xc0\xd9\xa4\xeb\xd0\x0e\x76\x32\x0c\x36\xe4\xd4\x58\xab\x5f\xb5\x31\xa7\x38\x34\xbd\xda\x60\xa3\x36\xcc\xd7\xde\x26\xf0\x1d\xd7\x0b\xd0\xfe\x29\xcc\x37\xa9\x6c\xd9\x73\xb3\xf6\xb1\x7b\xeb\xe5\x2f\x96\x47\xcc\xce\x3c\xb2\xc7\xc7\x37\xd8\xc7\xde\x1c\x97\xa1\x9e\x76\x79\xad\x3d\xb4\xc0\x4b\x1c\x60\x34\x77\x36\x73\x67\x81\x2d\xf9\xd9\x3d\x12\xc6\x20\xb6\xa1\xa8\x9f\x85\x3c\x50\xe8\x81\xf3\x18\x08\xab\x97\x23\xd8\x93\x63\xac\xc2\xe2\x9c\xb6\x43\xad\xca\xb6\xed\x53\x33\x7b\x94\x20\x6c\x68\x95\x0a\x48\x62\xa6\x75\xa7\xe8\x09\xda\xd4\x48\x45\xf6\xfb\xf6\xb2\x21\x0a\x16\x09\xdb\x71\x83\xa2\x4a\xd8\x5c\x62\x27\x80\x28\x88\x42\xa2\x90\xa2\x09\x48\x45\x92\xb4\xb6\x26\x3c\xa2\x7b\x6a\x34\xef\x38\x53\xc0\x54\xb5\xf3\x5f\xad\x6a\x74\xa2\xfc\x28\xc3\x4e\x3f\xbd\x2e\x15\x22\x33\x93\x79\x54\x8f\x5c\x18\xcd\x90\x3c\x40\x84\xbe\x49\xce\x3a\x0e\xcf\x8d\x32\xb7\xf6\x94\x42\x93\x44\x0b\x64\x46\x91\xd6\xc8\x7b\x65\x5a\x66\xab\x9d\x50\x4d\x60\x32\x65\x1a\x91\x54\x1c\xf5\x02\xc9\xc5\x3d\x80\x62\x83\x03\xe4\xdd\x2f\x97\x96\x6a\x2b\x0a\x7b\x98\x4b\x89\x8b\x73\xd1\x51\xa6\xe0\x00\x02\xdd\x62\x0f\xfb\x4e\x80\x17\xc8\x59\xfe\xe7\x6c\x37\xc8\xd9\x20\x77\x81\xbd\xc0\x0d\xb6\x0d\x35\x72\xa1\xb7\x37\x2d\x32\x31\x65\x4f\xba\x86\xac\x4b\xdc\xa7\x2e\x47\x25\xbd\x8a\x42\x83\xdb\x9f\x24\x97\x91\x04\x0c\xcc\x46\x20\x77\x82\xa6\x8a\x72\x66\x1a\x2d\xca\xd0\x14\x67\x9e\x37\x60\xd3\xdc\xf2\xa2\xde\x60\xb7\x5e\xb2\xcd\xbe\xb7\x43\xcd\xd9\xf7\x66\xef\x0d\x49\x43\x9e\xef\xcb\xc0\xec\xb2\x46\x34\x99\xb1\x17\x4a\x3e\xd3\x58\x26\x9f\x09\x41\xc6\xa7\x11\x41\xc5\xb8\x15\x23\x99\xe3\xb8\xa5\x40\xc0\xf7\x23\x48\x25\xdf\x53\x03\x06\x6a\xfd\x50\xab\x68\xf6\x9a\x86\x8c\x0b\x84\x3d\x2d\x2d\x49\x63\xb0\xb0\xb7\x7b\x75\xfd\xea\xc3\x6b\x37\xb6\xa2\x2f\xf7\x60\x2f\x77\x18\x98\xf3\x2d\xd3\xe7\x83\xfa\xc5\x40\xce\xfc\x56\x2d\x54\x0f\x92\x19\x91\xe6\x34\x5a\x37\x35\x21\x2a\x42\x6d\x04\x53\x8b\xa6\x17\x4a\x39\xf3\x8d\xfb\x91\xc1\x93\x04\xd8\x2f\xcd\xe7\x22\x21\xc3\x7a\xdf\xb3\x7c\xc8\x8e\xb7\x9c\x3e\x3f\x73\x8e\x14\x2c\x98\x5b\x4b\xc5\x52\x7f\x83\x29\xea\xef\xa8\x22\x90\x20\x4e\xad\x2f\xa2\xd1\x46\x0f\x9a\x86\x24\x8a\x04\x48\xed\x4f\x8b\x7f\xf4\x8f\x08\x2e\x54\x5b\x19\x29\x91\xf2\x99\x8b\xc8\x30\x0e\x92\x18\x84\x92\xe1\x50\xaf\xe8\x9f\x1a\x29\xdb\xf3\xc1\x2d\x72\xa7\xa1\xba\x2e\x41\x29\xca\x0e\x72\xd0\xd1\x52\xb3\xf9\xf5\x06\xeb\x6a\x8b\x84\x31\xca\xeb\x50\x35\xfb\xe0\x19\xda\x04\x43\x8f\x9d\x11\x4d\x23\xc0\x1f\x41\x8f\x2e\x97\x7e\x5c\xe3\x96\xfd\x21\x5c\x4d\x85\x1a\xb1\x75\x48\x79\x4c\x7c\x65\x49\x9a\xaf\x57\x2b\x37\xb8\xba\xf8\x11\x00\x00\xff\xff\xb3\xc0\x11\x7f\x4a\x15\x00\x00")
|
|
||||||
|
|
||||||
func _000001_init_schemaUpSqlBytes() ([]byte, error) {
|
|
||||||
return bindataRead(
|
|
||||||
__000001_init_schemaUpSql,
|
|
||||||
"000001_init_schema.up.sql",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _000001_init_schemaUpSql() (*asset, error) {
|
|
||||||
bytes, err := _000001_init_schemaUpSqlBytes()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
info := bindataFileInfo{name: "000001_init_schema.up.sql", size: 5450, mode: os.FileMode(420), modTime: time.Unix(1611216889, 0)}
|
|
||||||
a := &asset{bytes: bytes, info: info}
|
|
||||||
return a, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Asset loads and returns the asset for the given name.
|
|
||||||
// It returns an error if the asset could not be found or
|
|
||||||
// could not be loaded.
|
|
||||||
func Asset(name string) ([]byte, error) {
|
|
||||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
|
||||||
if f, ok := _bindata[cannonicalName]; ok {
|
|
||||||
a, err := f()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
|
|
||||||
}
|
|
||||||
return a.bytes, nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("Asset %s not found", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustAsset is like Asset but panics when Asset would return an error.
|
|
||||||
// It simplifies safe initialization of global variables.
|
|
||||||
func MustAsset(name string) []byte {
|
|
||||||
a, err := Asset(name)
|
|
||||||
if err != nil {
|
|
||||||
panic("asset: Asset(" + name + "): " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssetInfo loads and returns the asset info for the given name.
|
|
||||||
// It returns an error if the asset could not be found or
|
|
||||||
// could not be loaded.
|
|
||||||
func AssetInfo(name string) (os.FileInfo, error) {
|
|
||||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
|
||||||
if f, ok := _bindata[cannonicalName]; ok {
|
|
||||||
a, err := f()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
|
|
||||||
}
|
|
||||||
return a.info, nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("AssetInfo %s not found", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssetNames returns the names of the assets.
|
|
||||||
func AssetNames() []string {
|
|
||||||
names := make([]string, 0, len(_bindata))
|
|
||||||
for name := range _bindata {
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
return names
|
|
||||||
}
|
|
||||||
|
|
||||||
// _bindata is a table, holding each asset generator, mapped to its name.
|
|
||||||
var _bindata = map[string]func() (*asset, error){
|
|
||||||
"000001_init_schema.down.sql": _000001_init_schemaDownSql,
|
|
||||||
"000001_init_schema.up.sql": _000001_init_schemaUpSql,
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssetDir returns the file names below a certain
|
|
||||||
// directory embedded in the file by go-bindata.
|
|
||||||
// For example if you run go-bindata on data/... and data contains the
|
|
||||||
// following hierarchy:
|
|
||||||
// data/
|
|
||||||
// foo.txt
|
|
||||||
// img/
|
|
||||||
// a.png
|
|
||||||
// b.png
|
|
||||||
// then AssetDir("data") would return []string{"foo.txt", "img"}
|
|
||||||
// AssetDir("data/img") would return []string{"a.png", "b.png"}
|
|
||||||
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
|
|
||||||
// AssetDir("") will return []string{"data"}.
|
|
||||||
func AssetDir(name string) ([]string, error) {
|
|
||||||
node := _bintree
|
|
||||||
if len(name) != 0 {
|
|
||||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
|
||||||
pathList := strings.Split(cannonicalName, "/")
|
|
||||||
for _, p := range pathList {
|
|
||||||
node = node.Children[p]
|
|
||||||
if node == nil {
|
|
||||||
return nil, fmt.Errorf("Asset %s not found", name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if node.Func != nil {
|
|
||||||
return nil, fmt.Errorf("Asset %s not found", name)
|
|
||||||
}
|
|
||||||
rv := make([]string, 0, len(node.Children))
|
|
||||||
for childName := range node.Children {
|
|
||||||
rv = append(rv, childName)
|
|
||||||
}
|
|
||||||
return rv, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type bintree struct {
|
|
||||||
Func func() (*asset, error)
|
|
||||||
Children map[string]*bintree
|
|
||||||
}
|
|
||||||
|
|
||||||
var _bintree = &bintree{nil, map[string]*bintree{
|
|
||||||
"000001_init_schema.down.sql": &bintree{_000001_init_schemaDownSql, map[string]*bintree{}},
|
|
||||||
"000001_init_schema.up.sql": &bintree{_000001_init_schemaUpSql, map[string]*bintree{}},
|
|
||||||
}}
|
|
||||||
|
|
||||||
// RestoreAsset restores an asset under the given directory
|
|
||||||
func RestoreAsset(dir, name string) error {
|
|
||||||
data, err := Asset(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
info, err := AssetInfo(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RestoreAssets restores an asset under the given directory recursively
|
|
||||||
func RestoreAssets(dir, name string) error {
|
|
||||||
children, err := AssetDir(name)
|
|
||||||
// File
|
|
||||||
if err != nil {
|
|
||||||
return RestoreAsset(dir, name)
|
|
||||||
}
|
|
||||||
// Dir
|
|
||||||
for _, child := range children {
|
|
||||||
err = RestoreAssets(dir, filepath.Join(name, child))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func _filePath(dir, name string) string {
|
|
||||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
|
||||||
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
|
|
||||||
}
|
|
||||||
@@ -1,178 +0,0 @@
|
|||||||
package database
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot"
|
|
||||||
)
|
|
||||||
|
|
||||||
type patreonService struct {
|
|
||||||
db *sql.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s patreonService) PatreonCreatorByID(id int) (geeksbot.PatreonCreator, error) {
|
|
||||||
var creator geeksbot.PatreonCreator
|
|
||||||
var gID string
|
|
||||||
queryString := "SELECT id, creator, link, guild_id FROM patreon_creator WHERE id = $1"
|
|
||||||
row := s.db.QueryRow(queryString, id)
|
|
||||||
err := row.Scan(&creator.ID, &creator.Creator, &creator.Link, &gID)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.PatreonCreator{}, err
|
|
||||||
}
|
|
||||||
guild, err := GuildService.Guild(gID)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.PatreonCreator{}, err
|
|
||||||
}
|
|
||||||
creator.Guild = guild
|
|
||||||
return creator, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s patreonService) PatreonCreatorByName(name string, g geeksbot.Guild) (geeksbot.PatreonCreator, error) {
|
|
||||||
var id int
|
|
||||||
queryString := "SELECT id FROM patreon_creator WHERE creator = $1 AND guild_id = $2"
|
|
||||||
err := s.db.QueryRow(queryString, name, g.ID).Scan(&id)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.PatreonCreator{}, nil
|
|
||||||
}
|
|
||||||
creator, err := s.PatreonCreatorByID(id)
|
|
||||||
return creator, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s patreonService) CreatePatreonCreator(c geeksbot.PatreonCreator) (geeksbot.PatreonCreator, error) {
|
|
||||||
var id int
|
|
||||||
queryString := `INSERT INTO patreon_creator (creator, link, guild_id) VALUES ($1, $2, $3) RETURNING id`
|
|
||||||
err := s.db.QueryRow(queryString, c.Creator, c.Link, c.Guild.ID).Scan(&id)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.PatreonCreator{}, err
|
|
||||||
}
|
|
||||||
c.ID = id
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s patreonService) UpdatePatreonCreator(c geeksbot.PatreonCreator) (geeksbot.PatreonCreator, error) {
|
|
||||||
queryString := `UPDATE patreon_creator SET creator = $2, link = $3 WHERE id = $1`
|
|
||||||
_, err := s.db.Exec(queryString, c.ID, c.Creator, c.Link)
|
|
||||||
return c, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s patreonService) DeletePatreonCreator(c geeksbot.PatreonCreator) error {
|
|
||||||
queryString := `DELETE FROM patreon_creator WHERE id = $1`
|
|
||||||
_, err := s.db.Exec(queryString, c.ID)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s patreonService) PatreonTierByID(id int) (geeksbot.PatreonTier, error) {
|
|
||||||
var tier geeksbot.PatreonTier
|
|
||||||
var cID int
|
|
||||||
var rID string
|
|
||||||
var next int
|
|
||||||
queryString := `SELECT id, name, description, creator, role, next_tier FROM patreon_tier WHERE id = id`
|
|
||||||
err := s.db.QueryRow(queryString, id).Scan(&tier.ID, &tier.Name, &tier.Description, &cID, &rID, &next)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.PatreonTier{}, err
|
|
||||||
}
|
|
||||||
creator, err := s.PatreonCreatorByID(cID)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.PatreonTier{}, err
|
|
||||||
}
|
|
||||||
tier.Creator = creator
|
|
||||||
role, err := GuildService.Role(rID)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.PatreonTier{}, err
|
|
||||||
}
|
|
||||||
tier.Role = role
|
|
||||||
if next == -1 {
|
|
||||||
tier.NextTier = nil
|
|
||||||
return tier, nil
|
|
||||||
}
|
|
||||||
nextTier, err := s.PatreonTierByID(next)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.PatreonTier{}, err
|
|
||||||
}
|
|
||||||
tier.NextTier = &nextTier
|
|
||||||
return tier, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s patreonService) PatreonTierByName(name string, creator string) (geeksbot.PatreonTier, error) {
|
|
||||||
var id int
|
|
||||||
queryString := `SELECT id FROM patreon_tier WHERE name = $1 AND creator = $2`
|
|
||||||
err := s.db.QueryRow(queryString, name, creator).Scan(&id)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.PatreonTier{}, err
|
|
||||||
}
|
|
||||||
tier, err := s.PatreonTierByID(id)
|
|
||||||
return tier, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s patreonService) CreatePatreonTier(t geeksbot.PatreonTier) (geeksbot.PatreonTier, error) {
|
|
||||||
var id int
|
|
||||||
queryString := `INSERT INTO patreon_tier (name, description, creator, role, next_tier)
|
|
||||||
VALUES ($1, $2, $3, $4, $5) RETURNING id`
|
|
||||||
err := s.db.QueryRow(queryString, t.Name, t.Description, t.Creator.ID, t.Role.ID, t.NextTier.ID).Scan(&id)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.PatreonTier{}, err
|
|
||||||
}
|
|
||||||
t.ID = id
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s patreonService) UpdatePatreonTier(t geeksbot.PatreonTier) (geeksbot.PatreonTier, error) {
|
|
||||||
queryString := `UPDATE patreon_tier SET name = $2, description = $3, role = $4, next_tier = $5 WHERE id = $1`
|
|
||||||
_, err := s.db.Exec(queryString, t.ID, t.Name, t.Description, t.Role.ID, t.NextTier.ID)
|
|
||||||
return t, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s patreonService) DeletePatreonTier(t geeksbot.PatreonTier) error {
|
|
||||||
queryString := `DELETE FROM patreon_tier WHERE id = $1`
|
|
||||||
_, err := s.db.Exec(queryString, t.ID)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s patreonService) GuildPatreonCreators(g geeksbot.Guild) ([]geeksbot.PatreonCreator, error) {
|
|
||||||
var creators []geeksbot.PatreonCreator
|
|
||||||
queryString := `SELECT id FROM patreon_creator WHERE guild_id = $1`
|
|
||||||
rows, err := s.db.Query(queryString, g.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for rows.Next() {
|
|
||||||
var id int
|
|
||||||
err := rows.Scan(&id)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
creator, err := s.PatreonCreatorByID(id)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
creators = append(creators, creator)
|
|
||||||
}
|
|
||||||
return creators, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s patreonService) CreatorPatreonTiers(c geeksbot.PatreonCreator) ([]geeksbot.PatreonTier, error) {
|
|
||||||
var tiers []geeksbot.PatreonTier
|
|
||||||
queryString := `SELECT id FROM patreon_tier WHERE creator = $1`
|
|
||||||
rows, err := s.db.Query(queryString, c.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for rows.Next() {
|
|
||||||
var id int
|
|
||||||
err := rows.Scan(&id)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tier, err := s.PatreonTierByID(id)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tiers = append(tiers, tier)
|
|
||||||
}
|
|
||||||
return tiers, nil
|
|
||||||
}
|
|
||||||
@@ -1,238 +0,0 @@
|
|||||||
package database
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot"
|
|
||||||
)
|
|
||||||
|
|
||||||
type requestService struct {
|
|
||||||
db *sql.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s requestService) Request(id int64) (geeksbot.Request, error) {
|
|
||||||
var r geeksbot.Request
|
|
||||||
var aID string
|
|
||||||
var cID string
|
|
||||||
var gID string
|
|
||||||
var uID sql.NullString
|
|
||||||
var mID string
|
|
||||||
queryString := `SELECT id, author_id, channel_id, guild_id, content, requested_at, completed,
|
|
||||||
completed_at, completed_by, message_id, completed_message FROM requests
|
|
||||||
WHERE id = $1`
|
|
||||||
row := s.db.QueryRow(queryString, id)
|
|
||||||
err := row.Scan(&r.ID, &aID, &cID, &gID, &r.Content, &r.RequestedAt, &r.Completed,
|
|
||||||
&r.CompletedAt, &uID, &mID, &r.CompletedMessage)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Request{}, err
|
|
||||||
}
|
|
||||||
author, err := UserService.User(aID)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Request{}, err
|
|
||||||
}
|
|
||||||
guild, err := GuildService.Guild(gID)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Request{}, err
|
|
||||||
}
|
|
||||||
channel, err := ChannelService.Channel(cID)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Request{}, err
|
|
||||||
}
|
|
||||||
if !uID.Valid {
|
|
||||||
r.CompletedBy = nil
|
|
||||||
} else {
|
|
||||||
completedBy, err := UserService.User(uID.String)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Request{}, err
|
|
||||||
}
|
|
||||||
r.CompletedBy = &completedBy
|
|
||||||
}
|
|
||||||
message, err := MessageService.Message(mID)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Request{}, err
|
|
||||||
}
|
|
||||||
r.Author = author
|
|
||||||
r.Guild = guild
|
|
||||||
r.Channel = channel
|
|
||||||
r.Message = message
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s requestService) UserRequests(u geeksbot.User, completed bool) ([]geeksbot.Request, error) {
|
|
||||||
var requests []geeksbot.Request
|
|
||||||
var queryString string
|
|
||||||
if completed {
|
|
||||||
queryString = "SELECT id FROM requests WHERE author_id = $1"
|
|
||||||
} else {
|
|
||||||
queryString = "SELECT id FROM requests WHERE author_id = $1 AND completed = False"
|
|
||||||
}
|
|
||||||
rows, err := s.db.Query(queryString, u.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for rows.Next() {
|
|
||||||
var id int64
|
|
||||||
err = rows.Scan(&id)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
request, err := s.Request(id)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
requests = append(requests, request)
|
|
||||||
}
|
|
||||||
return requests, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s requestService) GuildRequests(g geeksbot.Guild, completed bool) ([]geeksbot.Request, error) {
|
|
||||||
var requests []geeksbot.Request
|
|
||||||
var queryString string
|
|
||||||
if completed {
|
|
||||||
queryString = "SELECT id FROM requests WHERE guild_id = $1"
|
|
||||||
} else {
|
|
||||||
queryString = "SELECT id FROM requests WHERE guild_id = $1 AND completed = False"
|
|
||||||
}
|
|
||||||
rows, err := s.db.Query(queryString, g.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for rows.Next() {
|
|
||||||
var id int64
|
|
||||||
err = rows.Scan(&id)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
request, err := s.Request(id)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
requests = append(requests, request)
|
|
||||||
}
|
|
||||||
return requests, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s requestService) CreateRequest(r geeksbot.Request) (geeksbot.Request, error) {
|
|
||||||
queryString := `INSERT INTO requests
|
|
||||||
(author_id, channel_id, guild_id, content, requested_at,
|
|
||||||
completed, completed_at, completed_by, message_id, completed_message)
|
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id`
|
|
||||||
var id int64
|
|
||||||
var completedByID sql.NullString
|
|
||||||
if r.CompletedBy == nil {
|
|
||||||
completedByID.String = ""
|
|
||||||
completedByID.Valid = false
|
|
||||||
} else {
|
|
||||||
completedByID.String = r.CompletedBy.ID
|
|
||||||
completedByID.Valid = true
|
|
||||||
}
|
|
||||||
err := s.db.QueryRow(queryString,
|
|
||||||
r.Author.ID,
|
|
||||||
r.Channel.ID,
|
|
||||||
r.Guild.ID,
|
|
||||||
r.Content,
|
|
||||||
r.RequestedAt,
|
|
||||||
r.Completed,
|
|
||||||
r.CompletedAt,
|
|
||||||
completedByID,
|
|
||||||
r.Message.ID,
|
|
||||||
r.CompletedMessage).Scan(&id)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Request{}, err
|
|
||||||
}
|
|
||||||
r.ID = id
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s requestService) UpdateRequest(r geeksbot.Request) (geeksbot.Request, error) {
|
|
||||||
queryString := `UPDATE requests SET
|
|
||||||
completed = $2, completed_at = $3, completed_by = $4, completed_message = $5
|
|
||||||
WHERE id = $1`
|
|
||||||
_, err := s.db.Exec(queryString, r.ID, r.Completed, r.CompletedAt, r.CompletedBy.ID, r.CompletedMessage)
|
|
||||||
return r, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s requestService) DeleteRequest(r geeksbot.Request) error {
|
|
||||||
queryString := "DELETE FROM requests WHERE id = $1"
|
|
||||||
_, err := s.db.Exec(queryString, r.ID)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s requestService) Comment(id int64) (geeksbot.Comment, error) {
|
|
||||||
var c geeksbot.Comment
|
|
||||||
var aID string
|
|
||||||
var rID int64
|
|
||||||
queryString := "SELECT id, author_id, request_id, comment_at, content FROM comments WHERE id = $1"
|
|
||||||
row := s.db.QueryRow(queryString, id)
|
|
||||||
err := row.Scan(&c.ID, &aID, &rID, &c.CommentAt, &c.Content)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Comment{}, err
|
|
||||||
}
|
|
||||||
author, err := UserService.User(aID)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Comment{}, err
|
|
||||||
}
|
|
||||||
c.Author = author
|
|
||||||
request, err := s.Request(rID)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Comment{}, err
|
|
||||||
}
|
|
||||||
c.Request = request
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s requestService) RequestComments(r geeksbot.Request) ([]geeksbot.Comment, error) {
|
|
||||||
var comments []geeksbot.Comment
|
|
||||||
queryString := "SELECT id FROM comments WHERE request_id = $1"
|
|
||||||
rows, err := s.db.Query(queryString, r.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for rows.Next() {
|
|
||||||
var id int64
|
|
||||||
err := rows.Scan(&id)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
comment, err := s.Comment(id)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
comments = append(comments, comment)
|
|
||||||
}
|
|
||||||
return comments, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s requestService) RequestCommentCount(r geeksbot.Request) (int, error) {
|
|
||||||
var count int
|
|
||||||
queryString := "SELECT COUNT(id) FROM comments WHERE request_id = $1"
|
|
||||||
row := s.db.QueryRow(queryString, r.ID)
|
|
||||||
err := row.Scan(&count)
|
|
||||||
return count, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s requestService) CreateComment(c geeksbot.Comment) (geeksbot.Comment, error) {
|
|
||||||
queryString := `INSERT INTO comments (author_id, request_id, comment_at, content)
|
|
||||||
VALUES ($1, $2, $3, $4) RETURNING id`
|
|
||||||
var id int64
|
|
||||||
err := s.db.QueryRow(queryString, c.Author.ID, c.Request.ID, c.CommentAt, c.Content).Scan(&id)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Comment{}, err
|
|
||||||
}
|
|
||||||
c.ID = id
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s requestService) DeleteComment(c geeksbot.Comment) error {
|
|
||||||
queryString := "DELETE FROM comments WHERE id = $1"
|
|
||||||
_, err := s.db.Exec(queryString, c.ID)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
package database
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot"
|
|
||||||
)
|
|
||||||
|
|
||||||
type serverService struct {
|
|
||||||
db *sql.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s serverService) ServerByID(id int) (geeksbot.Server, error) {
|
|
||||||
var server geeksbot.Server
|
|
||||||
var guildID string
|
|
||||||
var aChanID sql.NullString
|
|
||||||
var iChanID sql.NullString
|
|
||||||
var iMsgID sql.NullString
|
|
||||||
var sMsgID sql.NullString
|
|
||||||
queryString := `SELECT id, name, ip_address, port, password, alerts_channel_id,
|
|
||||||
guild_id, info_channel_id, info_message_id, settings_message_id,
|
|
||||||
ftp_port, ftp_username, ftp_password
|
|
||||||
FROM servers WHERE id = $1`
|
|
||||||
row := s.db.QueryRow(queryString, id)
|
|
||||||
err := row.Scan(&server.ID, &server.Name, &server.IPAddr, &server.Port, &server.Password,
|
|
||||||
&aChanID, &guildID, &iChanID, &iMsgID, &sMsgID, &server.FTPPort, &server.FTPUser, &server.FTPPass)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Server{}, err
|
|
||||||
}
|
|
||||||
guild, err := GuildService.Guild(guildID)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Server{}, err
|
|
||||||
}
|
|
||||||
if !aChanID.Valid {
|
|
||||||
server.AlertsChannel = nil
|
|
||||||
} else {
|
|
||||||
alertChannel, err := ChannelService.Channel(aChanID.String)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Server{}, err
|
|
||||||
}
|
|
||||||
server.AlertsChannel = &alertChannel
|
|
||||||
}
|
|
||||||
if !iChanID.Valid {
|
|
||||||
server.InfoChannel = nil
|
|
||||||
} else {
|
|
||||||
infoChannel, err := ChannelService.Channel(iChanID.String)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Server{}, err
|
|
||||||
}
|
|
||||||
server.InfoChannel = &infoChannel
|
|
||||||
}
|
|
||||||
if !iMsgID.Valid {
|
|
||||||
server.InfoMessage = nil
|
|
||||||
} else {
|
|
||||||
infoMessage, err := MessageService.Message(iMsgID.String)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Server{}, err
|
|
||||||
}
|
|
||||||
server.InfoMessage = &infoMessage
|
|
||||||
}
|
|
||||||
if !sMsgID.Valid {
|
|
||||||
server.SettingsMessage = nil
|
|
||||||
} else {
|
|
||||||
settingsMessage, err := MessageService.Message(sMsgID.String)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Server{}, err
|
|
||||||
}
|
|
||||||
server.SettingsMessage = &settingsMessage
|
|
||||||
}
|
|
||||||
server.Guild = guild
|
|
||||||
return server, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s serverService) ServerByName(name string, guild geeksbot.Guild) (geeksbot.Server, error) {
|
|
||||||
var id int
|
|
||||||
queryString := "SELECT id FROM servers WHERE LOWER(name) = LOWER($1) AND guild_id = $2"
|
|
||||||
row := s.db.QueryRow(queryString, name, guild.ID)
|
|
||||||
err := row.Scan(&id)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Server{}, err
|
|
||||||
}
|
|
||||||
server, err := s.ServerByID(id)
|
|
||||||
return server, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s serverService) CreateServer(server geeksbot.Server) (geeksbot.Server, error) {
|
|
||||||
var id int
|
|
||||||
queryString := `INSERT INTO servers (name, ip_address, port, password, alerts_channel_id,
|
|
||||||
guild_id, info_channel_id, info_message_id, settings_message_id)
|
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id`
|
|
||||||
err := s.db.QueryRow(queryString,
|
|
||||||
server.Name,
|
|
||||||
server.IPAddr,
|
|
||||||
server.Port,
|
|
||||||
server.Password,
|
|
||||||
server.AlertsChannel,
|
|
||||||
server.Guild,
|
|
||||||
server.InfoChannel,
|
|
||||||
server.InfoMessage,
|
|
||||||
server.SettingsMessage,
|
|
||||||
).Scan(&id)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.Server{}, err
|
|
||||||
}
|
|
||||||
server.ID = id
|
|
||||||
return server, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s serverService) DeleteServer(server geeksbot.Server) error {
|
|
||||||
queryString := `DELETE FROM servers WHERE id = $1`
|
|
||||||
_, err := s.db.Exec(queryString, server.ID)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s serverService) UpdateServer(server geeksbot.Server) (geeksbot.Server, error) {
|
|
||||||
queryString := `UPDATE servers SET name = $2, ip_address = $3, port = $4, password = $5,
|
|
||||||
alerts_channel_id = $6, info_channel_id = $7, info_message_id = $8,
|
|
||||||
settings_message_id = $9, ftp_port = $10, ftp_username = $11,
|
|
||||||
ftp_password = $12 WHERE id = $1`
|
|
||||||
_, err := s.db.Exec(queryString,
|
|
||||||
server.Name,
|
|
||||||
server.IPAddr,
|
|
||||||
server.Port,
|
|
||||||
server.Password,
|
|
||||||
server.AlertsChannel.ID,
|
|
||||||
server.InfoChannel.ID,
|
|
||||||
server.InfoMessage.ID,
|
|
||||||
server.SettingsMessage.ID,
|
|
||||||
server.FTPPort,
|
|
||||||
server.FTPUser,
|
|
||||||
server.FTPPass,
|
|
||||||
)
|
|
||||||
return server, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s serverService) GuildServers(g geeksbot.Guild) ([]geeksbot.Server, error) {
|
|
||||||
var servers []geeksbot.Server
|
|
||||||
queryString := `SELECT id FROM servers WHERE guild_id = $1`
|
|
||||||
rows, err := s.db.Query(queryString, g.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for rows.Next() {
|
|
||||||
var id int
|
|
||||||
err = rows.Scan(&id)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
server, err := s.ServerByID(id)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
servers = append(servers, server)
|
|
||||||
}
|
|
||||||
return servers, nil
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
package database
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot"
|
|
||||||
)
|
|
||||||
|
|
||||||
type userService struct {
|
|
||||||
db *sql.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s userService) User(id string) (geeksbot.User, error) {
|
|
||||||
var user geeksbot.User
|
|
||||||
queryString := "SELECT id, steam_id, active, staff, admin FROM users WHERE id = $1"
|
|
||||||
row := s.db.QueryRow(queryString, id)
|
|
||||||
err := row.Scan(&user.ID, &user.SteamID, &user.IsActive, &user.IsStaff, &user.IsAdmin)
|
|
||||||
return user, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s userService) CreateUser(u geeksbot.User) (geeksbot.User, error) {
|
|
||||||
queryString := "INSERT INTO users (id, steam_id, active, staff, admin) VALUES ($1, $2, $3, $4, $5)"
|
|
||||||
var err error
|
|
||||||
_, err = s.db.Exec(queryString, u.ID, u.SteamID, u.IsActive, u.IsStaff, u.IsAdmin)
|
|
||||||
return u, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s userService) DeleteUser(u geeksbot.User) error {
|
|
||||||
queryString := "DELETE FROM users WHERE id = $1"
|
|
||||||
_, err := s.db.Exec(queryString, u.ID)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s userService) UpdateUser(u geeksbot.User) (geeksbot.User, error) {
|
|
||||||
queryString := "UPDATE users SET steam_id = $2, active = $3, staff = $4, admin = $5 WHERE id = $1"
|
|
||||||
_, err := s.db.Exec(queryString, u.ID, u.SteamID, u.IsActive, u.IsStaff, u.IsAdmin)
|
|
||||||
return u, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s userService) GetOrCreateUser(id string) (geeksbot.User, error) {
|
|
||||||
user, err := s.User(id)
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
user, err = s.CreateUser(geeksbot.User{
|
|
||||||
ID: id,
|
|
||||||
IsActive: true,
|
|
||||||
IsAdmin: false,
|
|
||||||
IsStaff: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return user, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s userService) GetBySteamID(steamID string) (geeksbot.User, error) {
|
|
||||||
var id string
|
|
||||||
queryString := "SELECT id FROM users WHERE steam_id = $1"
|
|
||||||
err := s.db.QueryRow(queryString, steamID).Scan(&id)
|
|
||||||
if err != nil {
|
|
||||||
return geeksbot.User{}, err
|
|
||||||
}
|
|
||||||
user, err := s.User(id)
|
|
||||||
return user, err
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package services
|
|
||||||
|
|
||||||
import (
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot"
|
|
||||||
"gitea.wayfinderak.com/wayfinderak/geeksbot/pkg/database"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
GuildService geeksbot.GuildService
|
|
||||||
UserService geeksbot.UserService
|
|
||||||
ChannelService geeksbot.ChannelService
|
|
||||||
MessageService geeksbot.MessageService
|
|
||||||
PatreonService geeksbot.PatreonService
|
|
||||||
RequestService geeksbot.RequestService
|
|
||||||
ServerService geeksbot.ServerService
|
|
||||||
)
|
|
||||||
|
|
||||||
func InitializeServices() {
|
|
||||||
GuildService = database.GuildService
|
|
||||||
UserService = database.UserService
|
|
||||||
ChannelService = database.ChannelService
|
|
||||||
MessageService = database.MessageService
|
|
||||||
PatreonService = database.PatreonService
|
|
||||||
RequestService = database.RequestService
|
|
||||||
ServerService = database.ServerService
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
DATABASE_URL=postgresql://USER:PASSWORD@HOST:5432/geeksbot?sslmode=disable
|
|
||||||
DISCORD_TOKEN=replace-me
|
|
||||||
STEAM_API_KEY=replace-me
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
services:
|
|
||||||
geeksbot:
|
|
||||||
image: gitea.wayfinderak.com/wayfinderak/geeksbot:latest
|
|
||||||
container_name: geeksbot
|
|
||||||
restart: unless-stopped
|
|
||||||
env_file:
|
|
||||||
- ./portainer-app.env
|
|
||||||
mem_limit: 1g
|
|
||||||
cpus: "2.0"
|
|
||||||
-42
@@ -1,42 +0,0 @@
|
|||||||
package geeksbot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Request struct {
|
|
||||||
ID int64
|
|
||||||
Author User
|
|
||||||
Channel Channel
|
|
||||||
Guild Guild
|
|
||||||
Content string
|
|
||||||
RequestedAt time.Time
|
|
||||||
Completed bool
|
|
||||||
CompletedAt sql.NullTime
|
|
||||||
CompletedBy *User
|
|
||||||
Message Message
|
|
||||||
CompletedMessage sql.NullString
|
|
||||||
}
|
|
||||||
|
|
||||||
type Comment struct {
|
|
||||||
ID int64
|
|
||||||
Author User
|
|
||||||
Request Request
|
|
||||||
CommentAt time.Time
|
|
||||||
Content string
|
|
||||||
}
|
|
||||||
|
|
||||||
type RequestService interface {
|
|
||||||
Request(id int64) (Request, error)
|
|
||||||
UserRequests(u User, completed bool) ([]Request, error)
|
|
||||||
GuildRequests(g Guild, completed bool) ([]Request, error)
|
|
||||||
CreateRequest(r Request) (Request, error)
|
|
||||||
UpdateRequest(r Request) (Request, error)
|
|
||||||
DeleteRequest(r Request) error
|
|
||||||
Comment(id int64) (Comment, error)
|
|
||||||
RequestComments(r Request) ([]Comment, error)
|
|
||||||
RequestCommentCount(r Request) (int, error)
|
|
||||||
CreateComment(c Comment) (Comment, error)
|
|
||||||
DeleteComment(c Comment) error
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# Keep required jobs alive; only skip reviewed non-runtime documentation paths.
|
|
||||||
set -euo pipefail
|
|
||||||
base="${DELIVERY_BASE:-}"
|
|
||||||
head="$(git rev-parse HEAD)"
|
|
||||||
run=true
|
|
||||||
if [[ "${DELIVERY_REF:-}" != refs/tags/* && "$head" == "${DELIVERY_SHA:-}" && "${DELIVERY_EVENT:-}" =~ ^(push|pull_request)$ && "$base" =~ ^[0-9a-f]{40}$ && "$base" != 0000000000000000000000000000000000000000 ]]; then
|
|
||||||
if ! git cat-file -e "$base^{commit}" 2>/dev/null; then
|
|
||||||
if [[ -n "${DELIVERY_FETCH_TOKEN:-}" ]]; then
|
|
||||||
git -c "http.extraHeader=Authorization: token ${DELIVERY_FETCH_TOKEN}" fetch --quiet --depth=1 origin "$base" >/dev/null 2>&1 || true
|
|
||||||
else
|
|
||||||
git fetch --quiet --depth=1 origin "$base" >/dev/null 2>&1 || true
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if git cat-file -e "$base^{commit}" 2>/dev/null; then
|
|
||||||
paths="$(mktemp)"
|
|
||||||
trap 'rm -f -- "$paths"' EXIT
|
|
||||||
# Snapshot comparison is conservative when main advanced during a PR.
|
|
||||||
# Both rename sides remain visible, including names containing newlines.
|
|
||||||
if git diff --no-renames --name-only -z "$base" "$head" > "$paths"; then
|
|
||||||
run=false
|
|
||||||
while IFS= read -r -d '' path; do
|
|
||||||
case "$path" in
|
|
||||||
README.md|CHANGELOG.md|LICENSE|LICENSE.md) ;;
|
|
||||||
*) run=true; break ;;
|
|
||||||
esac
|
|
||||||
done < "$paths"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if [[ "$run" == false ]]; then
|
|
||||||
echo 'Only reviewed non-runtime documentation changed; build and validation are not applicable.'
|
|
||||||
else
|
|
||||||
echo 'Runtime, unknown inputs, or unavailable event history: run required validation.'
|
|
||||||
fi
|
|
||||||
if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
|
|
||||||
echo "run=$run" >> "$GITHUB_OUTPUT"
|
|
||||||
fi
|
|
||||||
printf '%s\n' "$run"
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package geeksbot
|
|
||||||
|
|
||||||
type Server struct {
|
|
||||||
ID int
|
|
||||||
Name string
|
|
||||||
IPAddr string
|
|
||||||
Port int
|
|
||||||
Password string
|
|
||||||
AlertsChannel *Channel
|
|
||||||
Guild Guild
|
|
||||||
InfoChannel *Channel
|
|
||||||
InfoMessage *Message
|
|
||||||
SettingsMessage *Message
|
|
||||||
FTPPort int
|
|
||||||
FTPUser string
|
|
||||||
FTPPass string
|
|
||||||
}
|
|
||||||
|
|
||||||
type ServerService interface {
|
|
||||||
ServerByID(id int) (Server, error)
|
|
||||||
ServerByName(name string, guild Guild) (Server, error)
|
|
||||||
CreateServer(s Server) (Server, error)
|
|
||||||
DeleteServer(s Server) error
|
|
||||||
UpdateServer(s Server) (Server, error)
|
|
||||||
GuildServers(g Guild) ([]Server, error)
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package geeksbot
|
|
||||||
|
|
||||||
import "database/sql"
|
|
||||||
|
|
||||||
type User struct {
|
|
||||||
ID string
|
|
||||||
SteamID sql.NullString
|
|
||||||
IsActive bool
|
|
||||||
IsStaff bool
|
|
||||||
IsAdmin bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserService interface {
|
|
||||||
User(id string) (User, error)
|
|
||||||
CreateUser(u User) (User, error)
|
|
||||||
DeleteUser(u User) error
|
|
||||||
UpdateUser(u User) (User, error)
|
|
||||||
GetOrCreateUser(id string) (User, error)
|
|
||||||
GetBySteamID(steamID string) (User, error)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user