- Handle discordgo.ForumTags;

This commit is contained in:
naudachu 2023-11-14 20:26:10 +05:00
parent 77b8bd8abb
commit 0df0b21198
12 changed files with 386 additions and 107 deletions

View File

@ -59,6 +59,22 @@ func Run(conf domain.Config, opts DiscordOptions) error {
return fmt.Errorf("cannot open the session: %v", err)
}
// UPDATE FORUM IF NEEDED:
forum, err := session.Channel(os.Getenv("TASKS_CHANNEL"))
if err != nil {
log.Print(err)
}
forum, err = session.ChannelEditComplex(forum.ID, &discordgo.ChannelEdit{
AvailableTags: &router.Tags,
})
if err != nil {
log.Fatal(err)
} else {
}
log.Println("Adding commands...")
var cmds []*discordgo.ApplicationCommand
var logString []string
@ -73,6 +89,7 @@ func Run(conf domain.Config, opts DiscordOptions) error {
log.Println("Following commands added:")
log.Println(logString)
defer session.Close()
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt)
@ -86,6 +103,5 @@ func Run(conf domain.Config, opts DiscordOptions) error {
log.Panicf("Cannot delete '%v' command: %v", h.Name, err)
}
}
return nil
}

View File

@ -6,6 +6,34 @@ import (
"github.com/bwmarrin/discordgo"
)
func (h *router) setFlag(s *discordgo.Session, i *discordgo.InteractionCreate, tag *discordgo.ForumTag) error {
th, err := s.Channel(i.ChannelID)
if err != nil {
return err
}
forum, err := s.Channel(th.ParentID)
if err != nil {
return err
}
// Проверка на существование тега в списке тегов:
if len(forum.AvailableTags) != 0 {
for _, some := range forum.AvailableTags {
if some.Name == tag.Name {
log.Print(tag.Name)
thE, err := s.ChannelEditComplex(i.ChannelID, &discordgo.ChannelEdit{
AppliedTags: &[]string{some.ID},
})
_, _ = thE, err
}
}
}
return nil
}
func (h *router) CreateExternalTask() CommandRoute {
return CommandRoute{
@ -38,9 +66,20 @@ func (h *router) StartTask() ComponentRoute {
return ComponentRoute{
Handler: func(s *discordgo.Session, i *discordgo.InteractionCreate) {
h.controller.UpdateTask(i.Message.ID, 0)
user := i.Member.User.Mention()
convertable, err := h.controller.UpdateTask(i.Message.ID, 0, user)
if err != nil {
}
newContent := convertable.ExtractDomain().StartedMessage()
if err != nil {
newContent += "\n `In progress` action produced with error:" + err.Error()
}
newMsg := discordgo.MessageEdit{
Content: &newContent,
Channel: i.ChannelID,
ID: i.Message.ID,
Components: []discordgo.MessageComponent{
@ -57,13 +96,12 @@ func (h *router) StartTask() ComponentRoute {
},
}
editedM, err := s.ChannelMessageEditComplex(&newMsg)
h.setFlag(s, i, &h.Tags[0])
_, err = s.ChannelMessageEditComplex(&newMsg)
if err != nil {
log.Println("edition NOT complete, ", err)
}
log.Print(editedM)
},
}
}
@ -72,20 +110,28 @@ func (h *router) CloseTask() ComponentRoute {
return ComponentRoute{
Handler: func(s *discordgo.Session, i *discordgo.InteractionCreate) {
h.controller.UpdateTask(i.Message.ID, 1)
user := i.Member.User.Mention()
convertable, err := h.controller.UpdateTask(i.Message.ID, 1, user)
newContent := convertable.ExtractDomain().ClosedMessage()
if err != nil {
newContent += "\n `Close` action produced with error:" + err.Error()
}
newMsg := discordgo.MessageEdit{
Content: &newContent,
Channel: i.ChannelID,
ID: i.Message.ID,
Components: []discordgo.MessageComponent{},
}
editedM, err := s.ChannelMessageEditComplex(&newMsg)
msgE, err := s.ChannelMessageEditComplex(&newMsg)
if err != nil {
log.Println("edition NOT complete, ", err)
}
log.Print(editedM)
_ = msgE
h.setFlag(s, i, &h.Tags[1])
},
}

View File

@ -13,13 +13,19 @@ func (h *router) Ping() CommandRoute {
Description: "pongs in a reply",
},
Handler: func(s *discordgo.Session, i *discordgo.InteractionCreate) {
log.Println("ok, I'm here..")
err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Content: "`pong`",
Title: "Pong reply",
TTS: false,
Content: "Pong to: " + i.Member.User.Mention(),
Components: []discordgo.MessageComponent{},
Embeds: []*discordgo.MessageEmbed{},
Files: []*discordgo.File{},
Flags: 0,
Choices: []*discordgo.ApplicationCommandOptionChoice{},
CustomID: "",
Title: "",
},
})
if err != nil {

View File

@ -11,6 +11,8 @@ import (
type router struct {
Commands []CommandRoute
Components []ComponentRoute
Tags []discordgo.ForumTag
controller controller.WorkflowController
conf *domain.DiscordConfig
}
@ -22,7 +24,7 @@ func InitRouter(wc controller.WorkflowController, conf *domain.DiscordConfig) *r
r.Commands = append(r.Commands,
// r.CreateRepoHandler(3),
// r.CreateFolderHandler(3),
// r.Ping(),
r.Ping(),
// r.CreateTicketHandler(3),
// r.InitProjectFromChannel(3),
// r.GetInfo(),
@ -34,6 +36,19 @@ func InitRouter(wc controller.WorkflowController, conf *domain.DiscordConfig) *r
)
r.controller = wc
r.conf = conf
r.Tags = append(
r.Tags,
discordgo.ForumTag{
Name: "В работе",
Moderated: true,
EmojiName: "👩‍🍳",
},
discordgo.ForumTag{
Name: "Готово",
Moderated: true,
EmojiName: "✅",
})
return &r
}

View File

@ -2,6 +2,7 @@ package handler
import (
"context"
"strconv"
"strings"
"ticket-pimp/internal/domain"
@ -11,9 +12,17 @@ import (
func (h *Handler) FarmTaskHandler(ctx context.Context, mu *tgb.MessageUpdate) error {
msgID := mu.Message.ID
var (
taskText string = ""
answer string = ""
)
taskText := strings.TrimSpace(strings.Replace(mu.Text, "/task", "", 1))
msgID := mu.Message.ID
if mu.Caption != "" {
taskText = strings.TrimSpace(strings.Replace(mu.Caption, "/task", "", 1))
} else {
taskText = strings.TrimSpace(strings.Replace(mu.Text, "/task", "", 1))
}
var summaryTail string
@ -36,42 +45,35 @@ func (h *Handler) FarmTaskHandler(ctx context.Context, mu *tgb.MessageUpdate) er
mu.Chat.ID.PeerID(),
)
err := h.controller.InitTask(t)
conv, err := h.controller.InitTask(t)
// Coda.io was deprecated!
// id, err := h.coda.CreateTask(t.Summary, t.Description, t.Creator, t.CreatorLink)
// if err != nil {
// answer := errorAnswer(err.Error())
// h.LogMessage(ctx, mu, answer)
// return mu.Answer(answer).ParseMode(tg.HTML).DoVoid(ctx)
// }
// if id == "" {
// answer := errorAnswer("task wasn't created")
// h.LogMessage(ctx, mu, answer)
// return mu.Answer(answer).ParseMode(tg.HTML).DoVoid(ctx)
// }
// err = mu.Answer(fmt.Sprintf("Задача с id: %s была создана, жду ссылку", id)).DoVoid(ctx)
// if err != nil {
// log.Println("бот не смог ответить про создание задачи")
// }
// url, err := h.coda.GetRowLink(id)
if err != nil {
answer := err.Error()
h.LogMessage(ctx, mu, answer)
return err
}
// t.URL = url
// answer := tg.HTML.Text(
// tg.HTML.Line(tg.HTML.Link("🤘 Задача", t.URL), "была создана!"))
// h.LogMessage(ctx, mu, answer)
// return mu.Answer(answer).
// ReplyToMessageID(msgID).ParseMode(tg.HTML).DisableWebPagePreview(true).DoVoid(ctx)
answer := tg.HTML.Text(
tg.HTML.Line("🤘 Задача была создана!"))
i := strconv.Itoa(int(conv.ID))
answer = tg.HTML.Text(
tg.HTML.Line(
tg.HTML.Bold("Task ID: "),
tg.HTML.Code(i),
tg.HTML.Text(" was created"),
),
)
if mu.Caption != "" {
answer = tg.HTML.Text(
tg.HTML.Line(
tg.HTML.Bold("I'm unable to work with files, but"),
),
tg.HTML.Line(
tg.HTML.Bold("Task ID: "),
tg.HTML.Code(i),
tg.HTML.Text(" was created"),
),
)
}
h.LogMessage(ctx, mu, answer)
return mu.Answer(answer).
ReplyToMessageID(msgID).ParseMode(tg.HTML).DisableWebPagePreview(true).DoVoid(ctx)

View File

@ -3,8 +3,8 @@ package controller
import (
"context"
"fmt"
"log"
"os"
"strconv"
"ticket-pimp/internal/domain"
"ticket-pimp/internal/storage/db"
"time"
@ -13,23 +13,9 @@ import (
"github.com/jackc/pgx/v5/pgtype"
)
func (wc *WorkflowController) InitTask(t *domain.Task) error {
var (
token = os.Getenv("DISCORD_TOKEN")
channel = os.Getenv("TASKS_CHANNEL")
)
discord, err := discordgo.New("Bot " + token)
if err != nil {
log.Fatalf("unable to create discord session: %v", err)
// [ ] Что делать, если не получилось создать задачу?
}
func (wc *WorkflowController) InitTask(t *domain.Task) (*domain.Task, error) {
if err := discord.Open(); err != nil {
// [ ] Что делать, если не получилось создать задачу?
log.Printf("cannot open the session: %v", err)
}
// dbtask, err := wc.q.InsertTask(context.TODO(), pgtype.Text{String: st.ID, Valid: true})
// Записываем в базу созданную задачу ------------------------------------------
dbtask, err := wc.q.InsertTask(context.TODO(), db.InsertTaskParams{
Creator: pgtype.Text{String: t.Creator, Valid: true},
CreatorLink: pgtype.Text{
@ -42,18 +28,32 @@ func (wc *WorkflowController) InitTask(t *domain.Task) error {
},
})
if err != nil {
log.Println("unable to insert task")
return nil, fmt.Errorf("unable to create task at the db: %v", err)
}
// ------------------------------------------------------------------------------------
content := fmt.Sprintf(
"## TaskID: %d\nCreated by: %s\n\n%s",
dbtask.ID,
t.Creator,
t.Description,
task := newConvertable(&dbtask).ExtractDomain()
// Инициализируем новый клиент дискорда
// [ ] Нездоровое получение параметров клиента из os..
var (
token = os.Getenv("DISCORD_TOKEN")
forumChannelID = os.Getenv("TASKS_CHANNEL")
)
s, err := discordgo.New("Bot " + token)
if err != nil {
return task, fmt.Errorf("unable to create discord session: %v", err)
// [ ] Что делать, если не получилось создать задачу?
}
if err := s.Open(); err != nil {
return task, fmt.Errorf("cannot open the session: %v", err)
// [ ] Что делать, если не получилось создать задачу?
}
msg := discordgo.MessageSend{
Content: content,
Content: task.NotStartedMessage(),
Components: []discordgo.MessageComponent{
discordgo.ActionsRow{
Components: []discordgo.MessageComponent{
@ -74,28 +74,50 @@ func (wc *WorkflowController) InitTask(t *domain.Task) error {
},
}
st, err := discord.ChannelMessageSendComplex(channel, &msg)
th, err := s.ForumThreadStartComplex(
forumChannelID,
&discordgo.ThreadStart{
Name: "Task ID:" + strconv.Itoa(int(task.ID)),
},
&msg,
)
if err != nil {
log.Println("unable to send task message")
return task, fmt.Errorf("unable to update channel: %v", err)
}
_ = dbtask
_ = st
return err
err = wc.q.UpdateTaskWithMessageID(context.TODO(), db.UpdateTaskWithMessageIDParams{
Messageid: pgtype.Text{String: th.ID, Valid: true},
ID: dbtask.ID,
})
if err != nil {
return task, fmt.Errorf("unable to set discord message to task: %v", err)
}
func (wc *WorkflowController) UpdateTask(id string, opt int) {
return task, nil
}
func (wc *WorkflowController) UpdateTask(id string, opt int, user string) (*TaskConvertable, error) {
var (
err error
dbtask db.Task
)
switch opt {
case 0:
wc.q.StartTask(context.TODO(), db.StartTaskParams{
StartedAt: pgtype.Timestamptz{Time: time.Now(), InfinityModifier: 0, Valid: true},
dbtask, err = wc.q.StartTask(context.TODO(), db.StartTaskParams{
UpdatedAt: pgtype.Timestamptz{Time: time.Now(), InfinityModifier: 0, Valid: true},
Assignee: pgtype.Text{String: user, Valid: true},
Messageid: pgtype.Text{String: id, Valid: true},
})
return &TaskConvertable{&dbtask}, err
case 1:
wc.q.CloseTask(context.TODO(), db.CloseTaskParams{
ClosedAt: pgtype.Timestamptz{Time: time.Now(), InfinityModifier: 0, Valid: true},
dbtask, err = wc.q.CloseTask(context.TODO(), db.CloseTaskParams{
DeletedAt: pgtype.Timestamptz{Time: time.Now(), InfinityModifier: 0, Valid: true},
Assignee: pgtype.Text{String: user, Valid: true},
Messageid: pgtype.Text{String: id, Valid: true},
})
return &TaskConvertable{&dbtask}, err
}
return &TaskConvertable{&dbtask}, nil
}

View File

@ -5,6 +5,7 @@ import (
"ticket-pimp/internal/services"
"ticket-pimp/internal/storage/db"
"github.com/bwmarrin/discordgo"
"github.com/jackc/pgx/v5/pgxpool"
)
@ -14,6 +15,7 @@ type WorkflowController struct {
ICoda services.ICoda
pool *pgxpool.Pool
q *db.Queries
ATags []discordgo.ForumTag
}
func NewWorkflowController(
@ -35,3 +37,28 @@ type ProjectResponse struct {
Project *domain.Project
Message error
}
type TaskConvertable struct {
*db.Task
}
func newConvertable(db *db.Task) *TaskConvertable {
return &TaskConvertable{
Task: db,
}
}
func (t *TaskConvertable) ExtractDomain() *domain.Task {
return &domain.Task{
ID: t.ID,
// Summary: "",
Description: t.Description.String,
Creator: t.Creator.String,
CreatorLink: t.CreatorLink.String,
Assignee: t.Assignee.String,
CreatedAt: t.CreatedAt.Time,
DeletedAt: t.DeletedAt.Time,
UpdatedAt: t.UpdatedAt.Time,
// URL: "",
}
}

View File

@ -1,6 +1,9 @@
package domain
import "fmt"
import (
"fmt"
"time"
)
type Folder struct {
Title string // k
@ -51,14 +54,55 @@ func (r *Row) NewCell(col string, value string) *Row {
}
type Task struct {
ID int32
Summary string
Description string
Creator string
CreatorLink string
Assignee string
CreatedAt time.Time
DeletedAt time.Time
UpdatedAt time.Time
URL string
}
func (t *Task) NotStartedMessage() string {
return fmt.Sprintf(
"## TaskID: %d\nCreated by: %s\n>>> %s\n",
t.ID,
t.Creator,
t.Description,
)
}
func (t *Task) StartedMessage() string {
return fmt.Sprintf(
"## TaskID: %d\nCreated by: %s\n Assignee: %s\n🚀 Started at: %s\n>>> %s\n",
t.ID,
t.Creator,
t.Assignee,
t.UpdatedAt,
t.Description,
)
}
func (t *Task) ClosedMessage() string {
return fmt.Sprintf(
"## TaskID: %d\nCreated by: %s\n Assignee: %s\n✅ Closed at: %s\n>>> %s\n",
t.ID,
t.Creator,
t.Assignee,
t.DeletedAt,
t.Description,
)
}
func NewTask(summ, desc, c, cLink string) *Task {
return &Task{
Summary: summ,

View File

@ -19,8 +19,7 @@ type Task struct {
CreatorLink pgtype.Text
Messageid pgtype.Text
Description pgtype.Text
StartedAt pgtype.Timestamptz
ClosedAt pgtype.Timestamptz
Assignee pgtype.Text
CreatedAt pgtype.Timestamptz
DeletedAt pgtype.Timestamptz
UpdatedAt pgtype.Timestamptz

View File

@ -13,18 +13,19 @@ import (
const closeTask = `-- name: CloseTask :one
UPDATE tasks
SET closed_at = $1
WHERE messageID = $2
RETURNING id, creator, creator_link, messageid, description, started_at, closed_at, created_at, deleted_at, updated_at
SET deleted_at = $1, assignee = $2
WHERE messageID = $3
RETURNING id, creator, creator_link, messageid, description, assignee, created_at, deleted_at, updated_at
`
type CloseTaskParams struct {
ClosedAt pgtype.Timestamptz
DeletedAt pgtype.Timestamptz
Assignee pgtype.Text
Messageid pgtype.Text
}
func (q *Queries) CloseTask(ctx context.Context, arg CloseTaskParams) (Task, error) {
row := q.db.QueryRow(ctx, closeTask, arg.ClosedAt, arg.Messageid)
row := q.db.QueryRow(ctx, closeTask, arg.DeletedAt, arg.Assignee, arg.Messageid)
var i Task
err := row.Scan(
&i.ID,
@ -32,8 +33,7 @@ func (q *Queries) CloseTask(ctx context.Context, arg CloseTaskParams) (Task, err
&i.CreatorLink,
&i.Messageid,
&i.Description,
&i.StartedAt,
&i.ClosedAt,
&i.Assignee,
&i.CreatedAt,
&i.DeletedAt,
&i.UpdatedAt,
@ -102,6 +102,48 @@ func (q *Queries) GetConfig(ctx context.Context) (Appconfig, error) {
return i, err
}
const getTaskByID = `-- name: GetTaskByID :one
SELECT id, creator, creator_link, messageid, description, assignee, created_at, deleted_at, updated_at FROM tasks WHERE id = $1
`
func (q *Queries) GetTaskByID(ctx context.Context, id int32) (Task, error) {
row := q.db.QueryRow(ctx, getTaskByID, id)
var i Task
err := row.Scan(
&i.ID,
&i.Creator,
&i.CreatorLink,
&i.Messageid,
&i.Description,
&i.Assignee,
&i.CreatedAt,
&i.DeletedAt,
&i.UpdatedAt,
)
return i, err
}
const getTaskByMessage = `-- name: GetTaskByMessage :one
SELECT id, creator, creator_link, messageid, description, assignee, created_at, deleted_at, updated_at FROM tasks WHERE messageID = $1
`
func (q *Queries) GetTaskByMessage(ctx context.Context, messageid pgtype.Text) (Task, error) {
row := q.db.QueryRow(ctx, getTaskByMessage, messageid)
var i Task
err := row.Scan(
&i.ID,
&i.Creator,
&i.CreatorLink,
&i.Messageid,
&i.Description,
&i.Assignee,
&i.CreatedAt,
&i.DeletedAt,
&i.UpdatedAt,
)
return i, err
}
const getTicketByChannelID = `-- name: GetTicketByChannelID :one
SELECT id, key, channelid, project_git, build_git, folder, created_at, deleted_at, updated_at FROM tickets WHERE channelID = $1
`
@ -150,7 +192,7 @@ INSERT INTO tasks (
) VALUES (
$1, $2, $3
)
RETURNING id, creator, creator_link, messageid, description, started_at, closed_at, created_at, deleted_at, updated_at
RETURNING id, creator, creator_link, messageid, description, assignee, created_at, deleted_at, updated_at
`
type InsertTaskParams struct {
@ -168,8 +210,7 @@ func (q *Queries) InsertTask(ctx context.Context, arg InsertTaskParams) (Task, e
&i.CreatorLink,
&i.Messageid,
&i.Description,
&i.StartedAt,
&i.ClosedAt,
&i.Assignee,
&i.CreatedAt,
&i.DeletedAt,
&i.UpdatedAt,
@ -177,6 +218,40 @@ func (q *Queries) InsertTask(ctx context.Context, arg InsertTaskParams) (Task, e
return i, err
}
const listTasksByCreator = `-- name: ListTasksByCreator :many
SELECT id, creator, creator_link, messageid, description, assignee, created_at, deleted_at, updated_at FROM tasks WHERE creator_link = $1 AND deleted_at is NULL
`
func (q *Queries) ListTasksByCreator(ctx context.Context, creatorLink pgtype.Text) ([]Task, error) {
rows, err := q.db.Query(ctx, listTasksByCreator, creatorLink)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Task
for rows.Next() {
var i Task
if err := rows.Scan(
&i.ID,
&i.Creator,
&i.CreatorLink,
&i.Messageid,
&i.Description,
&i.Assignee,
&i.CreatedAt,
&i.DeletedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listTickets = `-- name: ListTickets :many
SELECT id, key, channelid, project_git, build_git, folder, created_at, deleted_at, updated_at FROM tickets WHERE deleted_at IS NULL
`
@ -260,18 +335,19 @@ func (q *Queries) SetNewConfig(ctx context.Context) (Appconfig, error) {
const startTask = `-- name: StartTask :one
UPDATE tasks
SET started_at = $1
WHERE messageID = $2
RETURNING id, creator, creator_link, messageid, description, started_at, closed_at, created_at, deleted_at, updated_at
SET updated_at = $1, assignee = $2
WHERE messageID = $3
RETURNING id, creator, creator_link, messageid, description, assignee, created_at, deleted_at, updated_at
`
type StartTaskParams struct {
StartedAt pgtype.Timestamptz
UpdatedAt pgtype.Timestamptz
Assignee pgtype.Text
Messageid pgtype.Text
}
func (q *Queries) StartTask(ctx context.Context, arg StartTaskParams) (Task, error) {
row := q.db.QueryRow(ctx, startTask, arg.StartedAt, arg.Messageid)
row := q.db.QueryRow(ctx, startTask, arg.UpdatedAt, arg.Assignee, arg.Messageid)
var i Task
err := row.Scan(
&i.ID,
@ -279,8 +355,7 @@ func (q *Queries) StartTask(ctx context.Context, arg StartTaskParams) (Task, err
&i.CreatorLink,
&i.Messageid,
&i.Description,
&i.StartedAt,
&i.ClosedAt,
&i.Assignee,
&i.CreatedAt,
&i.DeletedAt,
&i.UpdatedAt,
@ -288,6 +363,22 @@ func (q *Queries) StartTask(ctx context.Context, arg StartTaskParams) (Task, err
return i, err
}
const updateTaskWithMessageID = `-- name: UpdateTaskWithMessageID :exec
UPDATE tasks
SET messageID = $1
WHERE id = $2
`
type UpdateTaskWithMessageIDParams struct {
Messageid pgtype.Text
ID int32
}
func (q *Queries) UpdateTaskWithMessageID(ctx context.Context, arg UpdateTaskWithMessageIDParams) error {
_, err := q.db.Exec(ctx, updateTaskWithMessageID, arg.Messageid, arg.ID)
return err
}
const updateTicketBuildGit = `-- name: UpdateTicketBuildGit :one
UPDATE tickets
SET build_git = $1, updated_at = $2

View File

@ -6,10 +6,7 @@ CREATE TABLE tasks (
messageID VARCHAR(255),
description TEXT,
started_at TIMESTAMPTZ,
closed_at TIMESTAMPTZ,
assignee VARCHAR(255),
created_at TIMESTAMPTZ DEFAULT current_timestamp,
deleted_at TIMESTAMPTZ,

View File

@ -62,14 +62,28 @@ INSERT INTO tasks (
)
RETURNING *;
-- name: UpdateTaskWithMessageID :exec
UPDATE tasks
SET messageID = $1
WHERE id = $2;
-- name: StartTask :one
UPDATE tasks
SET started_at = $1
WHERE messageID = $2
SET updated_at = $1, assignee = $2
WHERE messageID = $3
RETURNING *;
-- name: CloseTask :one
UPDATE tasks
SET closed_at = $1
WHERE messageID = $2
SET deleted_at = $1, assignee = $2
WHERE messageID = $3
RETURNING *;
-- name: GetTaskByMessage :one
SELECT * FROM tasks WHERE messageID = $1;
-- name: ListTasksByCreator :many
SELECT * FROM tasks WHERE creator_link = $1 AND deleted_at is NULL;
-- name: GetTaskByID :one
SELECT * FROM tasks WHERE id = $1;