From 4d63d025333d3fb6d09225c79e9e1e80c23a1673 Mon Sep 17 00:00:00 2001 From: Ben Visness Date: Wed, 8 Dec 2021 23:00:44 -0600 Subject: [PATCH] Always update snippet tags on every Discord edit --- cinera/update_annotations.sh | 2 +- cinera/user_update_cinera.sh | 2 +- server/serversetup.sh | 8 +- src/discord/history.go | 2 +- src/discord/showcase.go | 164 ++++++++++++++++++++++------------- src/website/discord.go | 2 +- 6 files changed, 110 insertions(+), 70 deletions(-) diff --git a/cinera/update_annotations.sh b/cinera/update_annotations.sh index e22fb50..8fca0b3 100755 --- a/cinera/update_annotations.sh +++ b/cinera/update_annotations.sh @@ -7,7 +7,7 @@ fi . cinera.conf if [ ! -d $CINERA_HMML_PATH ]; then - git clone --config core.sshCommand="ssh -i ~/.ssh/gitlab-hmml" git@gitssh.handmade.network:Annotation-Pushers/cinera_handmade.network.git $CINERA_HMML_PATH + git clone --config core.sshCommand="ssh -i ~/.ssh/gitlab-hmml" git@git.handmade.network:Annotation-Pushers/cinera_handmade.network.git $CINERA_HMML_PATH fi if [ ! -d $CINERA_HMML_PATH ]; then diff --git a/cinera/user_update_cinera.sh b/cinera/user_update_cinera.sh index 0eb6f80..8b703ec 100755 --- a/cinera/user_update_cinera.sh +++ b/cinera/user_update_cinera.sh @@ -7,7 +7,7 @@ fi . cinera.conf if [ ! -d $CINERA_REPO_PATH ]; then - git clone --config core.sshCommand="ssh -i ~/.ssh/gitlab-annotation-system" git@gitssh.handmade.network:Annotation-Pushers/Annotation-System.git $CINERA_REPO_PATH + git clone --config core.sshCommand="ssh -i ~/.ssh/gitlab-annotation-system" git@git.handmade.network:Annotation-Pushers/Annotation-System.git $CINERA_REPO_PATH fi if [ ! -d $CINERA_REPO_PATH ]; then diff --git a/server/serversetup.sh b/server/serversetup.sh index a200fab..c2da639 100755 --- a/server/serversetup.sh +++ b/server/serversetup.sh @@ -176,7 +176,7 @@ if [ $checkpoint -lt 82 ]; then do_as hmn <<'SCRIPT' set -euxo pipefail - if ! ssh -T -i ~/.ssh/gitlab-hmn git@gitssh.handmade.network; then + if ! ssh -T -i ~/.ssh/gitlab-hmn git@git.handmade.network; then set +x echo "Copy the following key:" @@ -192,7 +192,7 @@ if [ $checkpoint -lt 82 ]; then SCRIPT do_as annotations <<'SCRIPT' - if ! ssh -T -i ~/.ssh/gitlab-annotation-system git@gitssh.handmade.network; then + if ! ssh -T -i ~/.ssh/gitlab-annotation-system git@git.handmade.network; then set +x echo "Copy the following key:" @@ -206,7 +206,7 @@ SCRIPT exit 1 fi - if ! ssh -T -i ~/.ssh/gitlab-hmml git@gitssh.handmade.network; then + if ! ssh -T -i ~/.ssh/gitlab-hmml git@git.handmade.network; then set +x echo "Copy the following key:" @@ -230,7 +230,7 @@ if [ $checkpoint -lt 90 ]; then set -euxo pipefail cd ~ - git clone git@gitssh.handmade.network:hmn/hmn.git + git clone git@git.handmade.network:hmn/hmn.git SCRIPT savecheckpoint 90 diff --git a/src/discord/history.go b/src/discord/history.go index 635096c..17458e4 100644 --- a/src/discord/history.go +++ b/src/discord/history.go @@ -200,7 +200,7 @@ func handleHistoryMessage(ctx context.Context, dbConn *pgxpool.Pool, msg *Messag } if createSnippets { if doSnippet, err := AllowedToCreateMessageSnippet(ctx, tx, newMsg.UserID); doSnippet && err == nil { - _, err := CreateMessageSnippet(ctx, tx, msg.ID) + _, err := CreateMessageSnippet(ctx, tx, newMsg.UserID, msg.ID) if err != nil { return err } diff --git a/src/discord/showcase.go b/src/discord/showcase.go index b3732e3..85d0fc1 100644 --- a/src/discord/showcase.go +++ b/src/discord/showcase.go @@ -26,7 +26,6 @@ var reDiscordMessageLink = regexp.MustCompile(`https?://.+?(\s|$)`) var errNotEnoughInfo = errors.New("Discord didn't send enough info in this event for us to do this") -// TODO: Turn this ad-hoc isJam parameter into a tag or something func (bot *botInstance) processShowcaseMsg(ctx context.Context, msg *Message) error { switch msg.Type { case MessageTypeDefault, MessageTypeReply, MessageTypeApplicationCommand: @@ -59,67 +58,10 @@ func (bot *botInstance) processShowcaseMsg(ctx context.Context, msg *Message) er return err } if doSnippet, err := AllowedToCreateMessageSnippet(ctx, tx, newMsg.UserID); doSnippet && err == nil { - snippet, err := CreateMessageSnippet(ctx, tx, msg.ID) + _, err := CreateMessageSnippet(ctx, tx, newMsg.UserID, msg.ID) if err != nil { return oops.New(err, "failed to create snippet in gateway") } - - u, err := FetchDiscordUser(ctx, bot.dbConn, newMsg.UserID) - if err != nil { - return oops.New(err, "failed to look up HMN user information from Discord user") - // we shouldn't see a "not found" here because of the AllowedToBlahBlahBlah check. - } - - projects, err := hmndata.FetchProjects(ctx, bot.dbConn, &u.HMNUser, hmndata.ProjectsQuery{ - OwnerIDs: []int{u.HMNUser.ID}, - }) - if err != nil { - return oops.New(err, "failed to look up user projects") - } - projectIDs := make([]int, len(projects)) - for i, p := range projects { - projectIDs[i] = p.Project.ID - } - - // Try to associate tags in the message with project tags in HMN. - // Match only tags for projects in which the current user is a collaborator. - messageTags := getDiscordTags(msg.Content) - type tagsRow struct { - Tag models.Tag `db:"tags"` - } - itUserTags, err := db.Query(ctx, tx, tagsRow{}, - ` - SELECT $columns - FROM - tags - JOIN handmade_project AS project ON project.tag = tags.id - JOIN handmade_user_projects AS user_project ON user_project.project_id = project.id - WHERE - project.id = ANY ($1) - `, - projectIDs, - ) - if err != nil { - return oops.New(err, "failed to fetch tags for user projects") - } - iUserTags := itUserTags.ToSlice() - - var tagIDs []int - for _, itag := range iUserTags { - tag := itag.(*tagsRow).Tag - for _, messageTag := range messageTags { - if tag.Text == messageTag { - tagIDs = append(tagIDs, tag.ID) - } - } - } - - for _, tagID := range tagIDs { - _, err = tx.Exec(ctx, `INSERT INTO snippet_tags (snippet_id, tag_id) VALUES ($1, $2)`, snippet.ID, tagID) - if err != nil { - return oops.New(err, "failed to add tag to snippet") - } - } } else if err != nil { return oops.New(err, "failed to check snippet permissions in gateway") } @@ -612,7 +554,14 @@ any content saved, nothing will happen. Does not check user preferences around snippets. */ -func CreateMessageSnippet(ctx context.Context, tx db.ConnOrTx, msgID string) (*models.Snippet, error) { +func CreateMessageSnippet(ctx context.Context, tx db.ConnOrTx, userID, msgID string) (snippet *models.Snippet, err error) { + defer func() { + err := updateSnippetTags(ctx, tx, userID, snippet) + if err != nil { + logging.ExtractLogger(ctx).Error().Err(err).Msg("failed to update tags for Discord snippet") + } + }() + // Check for existing snippet, maybe return it type existingSnippetResult struct { Message models.DiscordMessage `db:"msg"` @@ -644,13 +593,14 @@ func CreateMessageSnippet(ctx context.Context, tx db.ConnOrTx, msgID string) (*m contentMarkdown := existing.MessageContent.LastContent contentHTML := parsing.ParseMarkdown(contentMarkdown, parsing.DiscordMarkdown) - _, err := tx.Exec(ctx, + iSnippet, err := db.QueryOne(ctx, tx, models.Snippet{}, ` UPDATE handmade_snippet SET description = $1, _description_html = $2 WHERE id = $3 + RETURNING $columns `, contentMarkdown, contentHTML, @@ -659,8 +609,10 @@ func CreateMessageSnippet(ctx context.Context, tx db.ConnOrTx, msgID string) (*m if err != nil { logging.ExtractLogger(ctx).Warn().Err(err).Msg("failed to update content of snippet on message edit") } + return iSnippet.(*models.Snippet), nil + } else { + return existing.Snippet, nil } - return existing.Snippet, nil } if existing.Message.SnippetCreated { @@ -716,6 +668,94 @@ func CreateMessageSnippet(ctx context.Context, tx db.ConnOrTx, msgID string) (*m return isnippet.(*models.Snippet), nil } +/* +Associates any Discord tags with website tags. Idempotent; will clear +out any existing tags and then add new ones. +*/ +func updateSnippetTags(ctx context.Context, dbConn db.ConnOrTx, userID string, snippet *models.Snippet) error { + tx, err := dbConn.Begin(ctx) + if err != nil { + return oops.New(err, "failed to start transaction") + } + defer tx.Rollback(ctx) + + u, err := FetchDiscordUser(ctx, tx, userID) + if err != nil { + return oops.New(err, "failed to look up HMN user information from Discord user") + // we shouldn't see a "not found" here because of the AllowedToBlahBlahBlah check earlier in the process + } + + projects, err := hmndata.FetchProjects(ctx, tx, &u.HMNUser, hmndata.ProjectsQuery{ + OwnerIDs: []int{u.HMNUser.ID}, + }) + if err != nil { + return oops.New(err, "failed to look up user projects") + } + projectIDs := make([]int, len(projects)) + for i, p := range projects { + projectIDs[i] = p.Project.ID + } + + // Delete any existing tags for this snippet + _, err = tx.Exec(ctx, + ` + DELETE FROM snippet_tags + WHERE snippet_id = $1 + `, + snippet.ID, + ) + if err != nil { + return oops.New(err, "failed to delete existing snippet tags") + } + + // Try to associate tags in the message with project tags in HMN. + // Match only tags for projects in which the current user is a collaborator. + messageTags := getDiscordTags(snippet.Description) + type tagsRow struct { + Tag models.Tag `db:"tags"` + } + itUserTags, err := db.Query(ctx, tx, tagsRow{}, + ` + SELECT $columns + FROM + tags + JOIN handmade_project AS project ON project.tag = tags.id + JOIN handmade_user_projects AS user_project ON user_project.project_id = project.id + WHERE + project.id = ANY ($1) + `, + projectIDs, + ) + if err != nil { + return oops.New(err, "failed to fetch tags for user projects") + } + iUserTags := itUserTags.ToSlice() + + var tagIDs []int + for _, itag := range iUserTags { + tag := itag.(*tagsRow).Tag + for _, messageTag := range messageTags { + if tag.Text == messageTag { + tagIDs = append(tagIDs, tag.ID) + } + } + } + + for _, tagID := range tagIDs { + _, err = tx.Exec(ctx, `INSERT INTO snippet_tags (snippet_id, tag_id) VALUES ($1, $2)`, snippet.ID, tagID) + if err != nil { + return oops.New(err, "failed to add tag to snippet") + } + } + + err = tx.Commit(ctx) + if err != nil { + return oops.New(err, "failed to commit transaction") + } + + return nil +} + // NOTE(ben): This is maybe redundant with the regexes we use for markdown. But // do we actually want to reuse those, or should we keep them separate? var RESnippetableUrl = regexp.MustCompile(`^https?://(youtu\.be|(www\.)?youtube\.com/watch)`) diff --git a/src/website/discord.go b/src/website/discord.go index d5439ee..a84ed31 100644 --- a/src/website/discord.go +++ b/src/website/discord.go @@ -173,7 +173,7 @@ func DiscordShowcaseBacklog(c *RequestContext) ResponseData { for _, imsgId := range msgIds { msgId := imsgId.(*messageIdQuery) - _, err := discord.CreateMessageSnippet(c.Context(), c.Conn, msgId.MessageID) + _, err := discord.CreateMessageSnippet(c.Context(), c.Conn, duser.UserID, msgId.MessageID) if err != nil { c.Logger.Warn().Err(err).Msg("failed to create snippet from showcase backlog") continue