Skip to content

Commit

Permalink
Merge branch 'release/v1.3.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
namnhce committed May 30, 2024
2 parents b55730b + d1938a5 commit 9cfd71b
Show file tree
Hide file tree
Showing 28 changed files with 638 additions and 29 deletions.
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ services:
# expose:
# - 8201
postgres:
image: postgres:11
image: postgres:13
restart: always
environment:
POSTGRES_DB: fortress_local
Expand All @@ -22,7 +22,7 @@ services:
- 25432

postgres_test:
image: postgres:11
image: postgres:13
restart: always
environment:
POSTGRES_DB: fortress_local_test
Expand Down
3 changes: 0 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ require (
github.com/SebastiaanKlippert/go-wkhtmltopdf v1.9.0
github.com/antchfx/htmlquery v1.3.0
github.com/bwmarrin/discordgo v0.27.1
github.com/cenkalti/backoff/v4 v4.2.1
github.com/consolelabs/mochi-go-sdk v0.0.7
github.com/dstotijn/go-notion v0.11.0
github.com/ethereum/go-ethereum v1.13.5
Expand All @@ -29,7 +28,6 @@ require (
github.com/jackc/pgtype v1.14.0
github.com/jinzhu/now v1.1.5
github.com/joho/godotenv v1.5.1
github.com/k0kubun/pp/v3 v3.2.0
github.com/lib/pq v1.10.9
github.com/matoous/go-nanoid v1.5.0
github.com/patrickmn/go-cache v2.1.0+incompatible
Expand Down Expand Up @@ -126,7 +124,6 @@ require (
github.com/leodido/go-urn v1.2.4 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
Expand Down
6 changes: 0 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,6 @@ github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c=
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
Expand Down Expand Up @@ -460,8 +458,6 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
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/pp/v3 v3.2.0 h1:h33hNTZ9nVFNP3u2Fsgz8JXiF5JINoZfFq4SvKJwNcs=
github.com/k0kubun/pp/v3 v3.2.0/go.mod h1:ODtJQbQcIRfAD3N+theGCV1m/CBxweERz2dapdz1EwA=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
Expand Down Expand Up @@ -515,7 +511,6 @@ github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
Expand Down Expand Up @@ -890,7 +885,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
33 changes: 33 additions & 0 deletions migrations/schemas/20240502144255-create_discord_events.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
-- +migrate Up
CREATE TABLE events (
id uuid PRIMARY KEY DEFAULT (uuid()),
name TEXT NOT NULL,
description TEXT,
date TIMESTAMP(6),
discord_event_id VARCHAR,
discord_channel_id VARCHAR,
discord_message_id VARCHAR,
discord_creator_id VARCHAR,
event_type VARCHAR,
created_at TIMESTAMP(6) DEFAULT (now()),
updated_at TIMESTAMP(6) DEFAULT (now()),
deleted_at TIMESTAMP(6) DEFAULT NULL
);

CREATE TABLE event_speakers (
event_id UUID NOT NULL,
discord_account_id UUID NOT NULL,
topic TEXT,
UNIQUE (event_id, discord_account_id)
);

ALTER TABLE event_speakers
ADD CONSTRAINT event_speakers_discord_event_id_fkey FOREIGN KEY (event_id) REFERENCES events (id);

ALTER TABLE event_speakers
ADD CONSTRAINT event_speakers_discord_account_id_fkey FOREIGN KEY (discord_account_id) REFERENCES discord_accounts (id);

-- +migrate Down

DROP TABLE event_speakers;
DROP TABLE events;
4 changes: 3 additions & 1 deletion migrations/seed/permissions.sql
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,6 @@ INSERT INTO public.permissions (id, deleted_at, created_at, updated_at, name, co
('e5a515aa-6f4f-4ac8-8eac-490a9a358833', null, '2023-07-19 16:35:12.475872', '2023-06-20 16:35:12.475872', 'Delivery Metrics Read', 'deliveryMetrics.read'),
('51a3ec4a-6bae-4f02-9d9b-89ba539346da', null, '2023-07-20 16:35:12.475872', '2023-06-20 16:35:12.475872', 'Delivery Metrics Leader Board Read', 'deliveryMetrics.leaderBoard.read'),
('fea2497c-694d-43d7-82cd-764d622a6706', null, '2023-07-26 16:35:12.475872', '2023-07-26 16:35:12.475872', 'Delivery Metrics Sync', 'deliveryMetrics.sync'),
('b4edad19-3e30-4e86-bd6e-07f535011aaf', null, '2023-07-26 16:35:12.475872', '2023-07-26 16:35:12.475872', 'Employee Discord Read', 'employees.discord.read');
('b4edad19-3e30-4e86-bd6e-07f535011aaf', null, '2023-07-26 16:35:12.475872', '2023-07-26 16:35:12.475872', 'Employee Discord Read', 'employees.discord.read'),
('1641106e-9a94-42fa-80e0-9d6978cd3596', null, '2024-05-29 15:41:12.475872', '2024-05-29 15:41:12.475872', 'Employee Discord Edit','employees.discord.edit'),
('b069e35c-1144-4554-9854-ff529506d4e5', null, '2024-05-29 15:41:12.475872', '2024-05-29 15:41:12.475872', 'Employee Discord Create','employees.discord.create');
3 changes: 2 additions & 1 deletion pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,8 @@ type DiscordWebhook struct {
}

type DiscordID struct {
DwarvesGuild string
DwarvesGuild string
EventsChannel string
}

type Invoice struct {
Expand Down
13 changes: 13 additions & 0 deletions pkg/constant/constant.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package constant

const (
RegexPatternDiscordChannelID = `<#(\d+)>`
RegexPatternDiscordID = `<@(\d+)>`
RegexPatternEmail = `\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+\b`
RegexPatternIcyReward = ` (\d+)`
RegexPatternNumber = `\d{18,}`
RegexPatternUrl = `((?:https?://)[^\s]+)`
RegexPatternGithub = `gh:(\w+)`
RegexPatternDescription = `d:"(.*?)"`
RegexPatternTime = `t:(\w+)`
)
192 changes: 188 additions & 4 deletions pkg/handler/discord/discord.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package discord

import (
"errors"
"fmt"
"math/rand"
"net/http"
"strings"
"time"

"github.com/bwmarrin/discordgo"
"github.com/gin-gonic/gin"
"gorm.io/gorm"

"github.com/dwarvesf/fortress-api/pkg/config"
"github.com/dwarvesf/fortress-api/pkg/controller"
"github.com/dwarvesf/fortress-api/pkg/handler/discord/request"
Expand All @@ -18,11 +22,11 @@ import (
bcModel "github.com/dwarvesf/fortress-api/pkg/service/basecamp/model"
"github.com/dwarvesf/fortress-api/pkg/service/mochiprofile"
"github.com/dwarvesf/fortress-api/pkg/store"
"github.com/dwarvesf/fortress-api/pkg/store/discordevent"
"github.com/dwarvesf/fortress-api/pkg/store/employee"
"github.com/dwarvesf/fortress-api/pkg/store/onleaverequest"
"github.com/dwarvesf/fortress-api/pkg/utils/timeutil"
"github.com/dwarvesf/fortress-api/pkg/view"
"github.com/gin-gonic/gin"
)

type handler struct {
Expand All @@ -47,6 +51,7 @@ func New(controller *controller.Controller, store *store.Store, repo store.DBRep

const (
discordReadingChannel = "1225085624260759622"
discordRandomChannel = "788084358991970337"
discordPlayGroundReadingChannel = "1119171172198797393"
)

Expand Down Expand Up @@ -349,11 +354,11 @@ func (h *handler) DeliveryMetricsReport(c *gin.Context) {
c.JSON(http.StatusOK, view.CreateResponse[any](nil, nil, nil, nil, "ok"))
}

// SyncMemo check if today is birthday of any employee in the system
// SyncMemo syncs memologs from the source memo.d.foundation
func (h *handler) SyncMemo(c *gin.Context) {
targetChannelID := discordPlayGroundReadingChannel
if h.config.Env == "prod" {
targetChannelID = discordReadingChannel
targetChannelID = discordRandomChannel
}

memos, err := h.controller.MemoLog.Sync()
Expand Down Expand Up @@ -409,7 +414,7 @@ func (h *handler) NotifyWeeklyMemos(c *gin.Context) {

targetChannelID := discordPlayGroundReadingChannel
if h.config.Env == "prod" {
targetChannelID = discordReadingChannel
targetChannelID = discordRandomChannel
}

_, err = h.service.Discord.SendWeeklyMemosMessage(h.config.Discord.IDs.DwarvesGuild, memos, weekRangeStr, targetChannelID)
Expand All @@ -421,3 +426,182 @@ func (h *handler) NotifyWeeklyMemos(c *gin.Context) {

c.JSON(http.StatusOK, view.CreateResponse[any](nil, nil, nil, nil, "ok"))
}

// CreateScheduledEvent create new DF guild discord event
func (h *handler) CreateScheduledEvent(c *gin.Context) {
l := h.logger.Fields(
logger.Fields{
"handler": "discord",
"method": "CreateScheduledEvent",
},
)

in := request.DiscordEventInput{}
if err := c.ShouldBindJSON(&in); err != nil {
l.Error(err, "failed to decode body")
c.JSON(http.StatusBadRequest, view.CreateResponse[any](nil, nil, err, in, ""))
return
}

if err := in.Validate(); err != nil {
l.Errorf(err, "failed to validate data", "body", in)
c.JSON(http.StatusBadRequest, view.CreateResponse[any](nil, nil, err, in, ""))
return
}

// check if event already exists
if _, err := h.store.DiscordEvent.
One(h.repo.DB(),
&discordevent.Query{DiscordEventID: in.ID}); !errors.Is(err, gorm.ErrRecordNotFound) {
l.Errorf(err, "cannot find discord event", "body", in)
c.JSON(http.StatusBadRequest, view.CreateResponse[any](nil, nil, err, in, ""))
return
}

evtType, err := in.EventType()
if err != nil {
l.Errorf(err, "failed to set event type", "body", in)
c.JSON(http.StatusBadRequest, view.CreateResponse[any](nil, nil, err, in, ""))
return
}

// create event
_, err = h.store.DiscordEvent.Create(h.repo.DB(), &model.Event{
DiscordEventID: in.ID,
DiscordChannelID: in.DiscordChannelID,
DiscordCreatorID: in.DiscordCreatorID,
Name: in.Name,
Description: in.Description,
Date: in.Date,
EventType: evtType,
})
if err != nil {
h.logger.Error(err, "failed to create event")
c.JSON(http.StatusInternalServerError, view.CreateResponse[any](nil, nil, err, nil, ""))
return
}

c.JSON(http.StatusOK, view.CreateResponse[any](nil, nil, nil, nil, "ok"))
}

// ListScheduledEvent returns list of scheduled events
func (h *handler) ListScheduledEvent(c *gin.Context) {
l := h.logger.Fields(
logger.Fields{
"handler": "discord",
"method": "ListScheduledEvent",
},
)

var err error
after := time.Now().UTC()

afterStr := c.Query("after")
if strings.TrimSpace(afterStr) != "" {
after, err = time.Parse(time.RFC3339, afterStr)
if err != nil {
l.Error(err, "failed to parse query after to datetime")
c.JSON(http.StatusBadRequest, view.CreateResponse[any](nil, nil, err, nil, ""))
return
}
}

events, err := h.store.DiscordEvent.All(h.repo.DB(), &discordevent.Query{
After: &after,
}, false)
if err != nil {
l.Error(err, "failed to get events")
c.JSON(http.StatusInternalServerError, view.CreateResponse[any](nil, nil, err, nil, ""))
return
}

c.JSON(http.StatusOK, view.CreateResponse[any](events, nil, nil, nil, "ok"))
}

// SetScheduledEventSpeakers sets speakers for a scheduled event
func (h *handler) SetScheduledEventSpeakers(c *gin.Context) {
l := h.logger.Fields(
logger.Fields{
"handler": "discord",
"method": "SetScheduledEventSpeakers",
},
)

in := []request.DiscordEventSpeakerInput{}
if err := c.ShouldBindJSON(&in); err != nil {
l.Error(err, "failed to decode body")
c.JSON(http.StatusBadRequest, view.CreateResponse[any](nil, nil, err, in, ""))
return
}

for _, i := range in {
if err := i.Validate(); err != nil {
l.Errorf(err, "failed to validate data", "body", in)
c.JSON(http.StatusBadRequest, view.CreateResponse[any](nil, nil, err, in, ""))
return
}
}

// get event
discordEventID := c.Param("id")
event, err := h.store.DiscordEvent.One(h.repo.DB(), &discordevent.Query{DiscordEventID: discordEventID})
if err != nil {
l.Errorf(err, "failed to get event", "body", in)
c.JSON(http.StatusBadRequest, view.CreateResponse[any](nil, nil, err, in, ""))
return
}

// delete all speakers
if err = h.store.EventSpeaker.DeleteAllByEventID(h.repo.DB(), event.ID.String()); err != nil {
l.Errorf(err, "failed to delete all speakers", "body", in)
c.JSON(http.StatusInternalServerError, view.CreateResponse[any](nil, nil, err, in, ""))
return
}

event.EventSpeakers = make([]model.EventSpeaker, 0)

// get speakers
for _, i := range in {
speaker, err := h.store.DiscordAccount.OneByDiscordID(h.repo.DB(), i.ID)
if errors.Is(err, gorm.ErrRecordNotFound) {
discordUser, err := h.service.Discord.GetMember(i.ID)
if err != nil {
l.Errorf(err, "failed to get discord user", "body", in)
c.JSON(http.StatusInternalServerError, view.CreateResponse[any](nil, nil, err, in, ""))
return
}

speaker = &model.DiscordAccount{
DiscordID: i.ID,
DiscordUsername: discordUser.User.Username,
}

_, err = h.store.DiscordAccount.Upsert(h.repo.DB(), speaker)
if err != nil {
l.Errorf(err, "failed to upsert speaker", "body", in)
c.JSON(http.StatusInternalServerError, view.CreateResponse[any](nil, nil, err, in, ""))
return
}
} else if err != nil {
l.Errorf(err, "failed to get speaker", "body", in)
c.JSON(http.StatusInternalServerError, view.CreateResponse[any](nil, nil, err, in, ""))
return
}

event.EventSpeakers = append(event.EventSpeakers, model.EventSpeaker{
EventID: event.ID,
DiscordAccountID: speaker.ID,
Topic: i.Topic,
})
}

// set speakers
err = h.store.DiscordEvent.SetSpeakers(h.repo.DB(), event)
if err != nil {
l.Errorf(err, "failed to set speakers", "body", in)
c.JSON(http.StatusInternalServerError, view.CreateResponse[any](nil, nil, err, in, ""))
return
}

c.JSON(http.StatusOK, view.CreateResponse[any](view.ToDiscordEvent(*event), nil, nil, nil, ""))
}
6 changes: 6 additions & 0 deletions pkg/handler/discord/errs/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,10 @@ import "errors"
var (
ErrEmptyReportView = errors.New("view is empty")
ErrEmptyChannelID = errors.New("channelID is empty")
ErrEmptyGuildID = errors.New("guildID is empty")
ErrEmptyCreatorID = errors.New("creatorID is empty")
ErrEmptyName = errors.New("name is empty")
ErrEmptyDate = errors.New("date is nil")
ErrEmptyID = errors.New("discord user id is nil")
ErrEmptyTopic = errors.New("topic is nil")
)
Loading

0 comments on commit 9cfd71b

Please sign in to comment.