Compare commits

...

10 Commits

Author SHA1 Message Date
Dustin Pianalto
14390c5c21 Update deployment and action
Some checks failed
CI / build (push) Has been cancelled
2021-10-02 16:42:40 -08:00
Dustin Pianalto
9436a4385f Change token env variable
Some checks failed
CI / build (push) Has been cancelled
2021-01-19 01:56:56 -09:00
Dustin Pianalto
3e77b98be4 change deployment name
Some checks failed
CI / build (push) Has been cancelled
2021-01-19 01:50:40 -09:00
Dustin Pianalto
2c5d4a62aa Add missing deps
Some checks failed
CI / build (push) Has been cancelled
2021-01-19 01:02:46 -09:00
Dustin Pianalto
5abaf88102 Add missing deps 2021-01-19 00:59:58 -09:00
Dustin Pianalto
f1980e9bcb Remove missing deps 2021-01-19 00:56:21 -09:00
Dustin Pianalto
e0de5433df Basic bot and kubernetes deployment 2021-01-19 00:51:30 -09:00
Dustin Pianalto
6d67134fb7 Initial structure 2020-12-17 16:43:34 -09:00
Dusty.P
8b67892946
Update and rename README.md to README 2020-12-15 23:00:56 -09:00
Dusty.P
9c7b24b9d7
Create LICENSE 2020-12-15 23:00:28 -09:00
14 changed files with 638 additions and 0 deletions

64
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,64 @@
name: CI
# Controls when the action will run. Triggers the workflow on push to master or development
# with a tag like v1.0.0 or v1.0.0-dev
on:
push:
tags:
- v[0-9]+.[0-9]+.[0-9]+
- v[0-9]+.[0-9]+.[0-9]+-[a-zA-Z]+
jobs:
build:
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- name: Get Version
id: get_version
uses: battila7/get-version-action@v2.0.0
- name: install buildx
id: buildx
uses: crazy-max/ghaction-docker-buildx@v1
with:
version: latest
- name: Docker Login
# You may pin to the exact commit or the version.
# uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
uses: docker/login-action@v1.10.0
with:
registry: ${{ secrets.DR_URL }}
# Username used to log against the Docker registry
username: ${{ secrets.DH_USERNAME }}
# Password or personal access token used to log against the Docker registry
password: ${{ secrets.DH_PASSWORD }}
# Log out from the Docker registry at the end of a job
logout: true
- name: Docker Build & Push
env:
IMAGE_TAG: ${{ steps.get_version.outputs.version-without-v }}
run: |
docker buildx build --push \
--tag ${{ secrets.DR_URL }}/geeksbot:$IMAGE_TAG \
--platform linux/amd64,linux/arm/v7,linux/arm64 .
- name: Update deployment file
run: TAG=${{ steps.get_version.outputs.version-without-v }} && sed -i 's|<IMAGE>|${{ secrets.DR_URL }}/geeksbot:'${TAG}'|' $GITHUB_WORKSPACE/deployment.yml
- uses: azure/k8s-set-context@v1
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBE_CONFIG }}
id: setcontext
- name: Deploy to Kubernetes
run: kubectl apply -f $GITHUB_WORKSPACE/deployment.yml
- name: Verify deployment
run: kubectl rollout status deployment/geeksbot

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.env
.idea

20
Dockerfile Normal file
View File

@ -0,0 +1,20 @@
FROM golang:1.14-alpine as dev
WORKDIR /go/src/Geeksbot
COPY ./go.mod .
COPY ./go.sum .
RUN go mod download
COPY . .
RUN go install github.com/dustinpianalto/geeksbot/...
CMD [ "go", "run", "cmd/geeksbot/main.go"]
from alpine
WORKDIR /bin
COPY --from=dev /go/bin/geeksbot ./geeksbot
CMD [ "geeksbot" ]

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Dusty.P
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1
README Normal file
View File

@ -0,0 +1 @@
# Geeksbot

View File

101
cmd/geeksbot/main.go Normal file
View File

@ -0,0 +1,101 @@
package main
import (
"log"
"os"
"os/signal"
"syscall"
"github.com/bwmarrin/discordgo"
"github.com/dustinpianalto/disgoman"
"github.com/dustinpianalto/geeksbot/internal/exts"
)
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.Identify = discordgo.Identify{
Intents: discordgo.MakeIntent(discordgo.IntentsAll),
}
//postgres.ConnectDatabase(os.Getenv("DATABASE_URL"))
//postgres.InitializeDatabase()
//utils.LoadTestData()
//us := &postgres.UserService{DB: postgres.DB}
//gs := &postgres.GuildService{DB: postgres.DB}
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)
//services.InitalizeServices(us, gs)
//if _, ok := handler.Commands["help"]; !ok {
// handler.AddDefaultHelpCommand()
//}
dg.AddHandler(manager.OnMessage)
dg.AddHandler(manager.StatusManager.OnReady)
//dg.AddHandler(guild_management.OnMessageUpdate)
//dg.AddHandler(guild_management.OnMessageDelete)
//dg.AddHandler(user_management.OnGuildMemberAddLogging)
//dg.AddHandler(user_management.OnGuildMemberRemoveLogging)
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)
// Start the Logging handler in a goroutine
//go logging.LoggingHandler(logging.LoggingChannel)
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 {
return []string{"G.", "g."}
}
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)
}
}

45
deployment.yml Normal file
View File

@ -0,0 +1,45 @@
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: 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-2

8
go.mod Normal file
View File

@ -0,0 +1,8 @@
module github.com/dustinpianalto/geeksbot
go 1.14
require (
github.com/bwmarrin/discordgo v0.22.1
github.com/dustinpianalto/disgoman v0.0.15
)

10
go.sum Normal file
View File

@ -0,0 +1,10 @@
github.com/bwmarrin/discordgo v0.20.2/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q=
github.com/bwmarrin/discordgo v0.22.1 h1:254fNYyfqJWKbPzO5g8j/nUvRgj4dNlI19EB8rnkpt8=
github.com/bwmarrin/discordgo v0.22.1/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
github.com/dustinpianalto/disgoman v0.0.15 h1:kdIw6jhC82WBut7+4BarqxBw06dozU+Hu47LQzkkoGM=
github.com/dustinpianalto/disgoman v0.0.15/go.mod h1:v3FM6n+4dH9XlvO+IDx6MN3DUnGq6YVDBvy1A1k202g=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=

View File

@ -0,0 +1,64 @@
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)
}

View File

@ -0,0 +1,32 @@
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),
}
}

22
internal/exts/init.go Normal file
View File

@ -0,0 +1,22 @@
package exts
import (
"github.com/dustinpianalto/disgoman"
"github.com/dustinpianalto/geeksbot/internal/exts/utils"
)
func AddCommandHandlers(h *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
_ = h.AddCommand(utils.UserCommand)
_ = h.AddCommand(utils.SayCommand)
_ = h.AddCommand(utils.GitCommand)
_ = h.AddCommand(utils.InviteCommand)
_ = h.AddCommand(utils.PingCommand)
}

View File

@ -0,0 +1,248 @@
package utils
import (
"fmt"
"sort"
"strconv"
"strings"
"time"
"github.com/bwmarrin/discordgo"
"github.com/dustinpianalto/disgoman"
"github.com/dustinpianalto/geeksbot/internal/discord_utils"
)
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 Github",
Color: 0,
URL: "https://github.com/dustinpianalto/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,
}
guildJoinTime, _ := member.JoinedAt.Parse()
guildJoinedField := &discordgo.MessageEmbedField{
Name: "Joined Guild:",
Value: discord_utils.ParseDateString(guildJoinTime),
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,
}
}
}