Added a bunch of discord debugging

This commit is contained in:
Asaf Gartner 2024-03-28 21:24:46 +02:00
parent e8201a254e
commit 4568def378
9 changed files with 146 additions and 3 deletions

View File

@ -23,6 +23,34 @@ import (
"github.com/jpillora/backoff" "github.com/jpillora/backoff"
) )
type BotEvent struct {
Timestamp time.Time
Name string
Extra string
}
var botEvents = make([]BotEvent, 0, 1000)
var botEventsMutex = sync.Mutex{}
func RecordBotEvent(name, extra string) {
botEventsMutex.Lock()
defer botEventsMutex.Unlock()
if len(botEvents) > 1000 {
botEvents = botEvents[len(botEvents)-500:]
}
botEvents = append(botEvents, BotEvent{
Timestamp: time.Now(),
Name: name,
Extra: extra,
})
}
func GetBotEvents() []BotEvent {
botEventsMutex.Lock()
defer botEventsMutex.Unlock()
return botEvents[:]
}
func RunDiscordBot(ctx context.Context, dbConn *pgxpool.Pool) jobs.Job { func RunDiscordBot(ctx context.Context, dbConn *pgxpool.Pool) jobs.Job {
log := logging.ExtractLogger(ctx).With().Str("module", "discord").Logger() log := logging.ExtractLogger(ctx).With().Str("module", "discord").Logger()
ctx = logging.AttachLoggerToContext(&log, ctx) ctx = logging.AttachLoggerToContext(&log, ctx)
@ -56,6 +84,11 @@ func RunDiscordBot(ctx context.Context, dbConn *pgxpool.Pool) jobs.Job {
log.Info().Msg("Connecting to the Discord gateway") log.Info().Msg("Connecting to the Discord gateway")
bot := newBotInstance(dbConn) bot := newBotInstance(dbConn)
err := bot.Run(ctx) err := bot.Run(ctx)
disconnectMessage := ""
if err != nil {
disconnectMessage = err.Error()
}
RecordBotEvent("Disconnected", disconnectMessage)
if err != nil { if err != nil {
dur := boff.Duration() dur := boff.Duration()
log.Error(). log.Error().
@ -101,6 +134,8 @@ type botInstance struct {
conn *websocket.Conn conn *websocket.Conn
dbConn *pgxpool.Pool dbConn *pgxpool.Pool
resuming bool
heartbeatIntervalMs int heartbeatIntervalMs int
forceHeartbeat chan struct{} forceHeartbeat chan struct{}
@ -193,6 +228,7 @@ func (bot *botInstance) Run(ctx context.Context) (err error) {
logging.ExtractLogger(ctx).Info().Msg("Discord asked us to reconnect to the gateway") logging.ExtractLogger(ctx).Info().Msg("Discord asked us to reconnect to the gateway")
return nil return nil
case OpcodeInvalidSession: case OpcodeInvalidSession:
RecordBotEvent("Failed to resume - invalid session", "")
// We tried to resume but the session was invalid. // We tried to resume but the session was invalid.
// Delete the session and reconnect from scratch again. // Delete the session and reconnect from scratch again.
_, err := bot.dbConn.Exec(ctx, `DELETE FROM discord_session`) _, err := bot.dbConn.Exec(ctx, `DELETE FROM discord_session`)
@ -264,8 +300,11 @@ func (bot *botInstance) connect(ctx context.Context) error {
} }
} }
RecordBotEvent("Connected", "")
if shouldResume { if shouldResume {
RecordBotEvent("Resuming with session ID", session.ID)
// Reconnect to the previous session // Reconnect to the previous session
bot.resuming = true
err := bot.sendGatewayMessage(ctx, GatewayMessage{ err := bot.sendGatewayMessage(ctx, GatewayMessage{
Opcode: OpcodeResume, Opcode: OpcodeResume,
Data: Resume{ Data: Resume{
@ -540,11 +579,20 @@ func (bot *botInstance) processEventMsg(ctx context.Context, msg *GatewayMessage
panic(fmt.Sprintf("processEventMsg must only be used on Dispatch messages (opcode %d). Validate this before you call this function.", OpcodeDispatch)) panic(fmt.Sprintf("processEventMsg must only be used on Dispatch messages (opcode %d). Validate this before you call this function.", OpcodeDispatch))
} }
if bot.resuming {
name := ""
if msg.EventName != nil {
name = *msg.EventName
}
RecordBotEvent("Got event while resuming", name)
}
switch *msg.EventName { switch *msg.EventName {
case "RESUMED": case "RESUMED":
// Nothing to do, but at least we can log something // Nothing to do, but at least we can log something
logging.ExtractLogger(ctx).Info().Msg("Finished resuming gateway session") logging.ExtractLogger(ctx).Info().Msg("Finished resuming gateway session")
bot.resuming = false
RecordBotEvent("Done resuming", "")
bot.createApplicationCommands(ctx) bot.createApplicationCommands(ctx)
case "MESSAGE_CREATE": case "MESSAGE_CREATE":
newMessage := *MessageFromMap(msg.Data, "") newMessage := *MessageFromMap(msg.Data, "")

View File

@ -185,6 +185,7 @@ func Scrape(ctx context.Context, dbConn *pgxpool.Pool, channelID string, earlies
return true return true
} }
msg.Backfilled = true
err := HandleIncomingMessage(ctx, dbConn, &msg, createSnippets) err := HandleIncomingMessage(ctx, dbConn, &msg, createSnippets)
if err != nil { if err != nil {

View File

@ -285,8 +285,8 @@ func InternMessage(
_, err = dbConn.Exec(ctx, _, err = dbConn.Exec(ctx,
` `
INSERT INTO discord_message (id, channel_id, guild_id, url, user_id, sent_at, snippet_created) INSERT INTO discord_message (id, channel_id, guild_id, url, user_id, sent_at, snippet_created, backfilled)
VALUES ($1, $2, $3, $4, $5, $6, $7) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
`, `,
msg.ID, msg.ID,
msg.ChannelID, msg.ChannelID,
@ -295,6 +295,7 @@ func InternMessage(
msg.Author.ID, msg.Author.ID,
msg.Time(), msg.Time(),
false, false,
msg.Backfilled,
) )
if err != nil { if err != nil {
return oops.New(err, "failed to save new discord message") return oops.New(err, "failed to save new discord message")

View File

@ -275,6 +275,7 @@ type Message struct {
Embeds []Embed `json:"embeds"` Embeds []Embed `json:"embeds"`
originalMap map[string]interface{} originalMap map[string]interface{}
Backfilled bool
} }
func (m *Message) JumpURL() string { func (m *Message) JumpURL() string {

View File

@ -929,6 +929,12 @@ func BuildDiscordShowcaseBacklog() string {
return Url("/discord_showcase_backlog", nil) return Url("/discord_showcase_backlog", nil)
} }
var RegexDiscordBotDebugPage = regexp.MustCompile("^/discord_bot_debug$")
func BuildDiscordBotDebugPage() string {
return Url("/discord_bot_debug", nil)
}
/* /*
* API * API
*/ */

View File

@ -0,0 +1,47 @@
package migrations
import (
"context"
"time"
"git.handmade.network/hmn/hmn/src/migration/types"
"github.com/jackc/pgx/v5"
)
func init() {
registerMigration(AddBackfillToDiscordMessage{})
}
type AddBackfillToDiscordMessage struct{}
func (m AddBackfillToDiscordMessage) Version() types.MigrationVersion {
return types.MigrationVersion(time.Date(2024, 3, 28, 18, 41, 7, 0, time.UTC))
}
func (m AddBackfillToDiscordMessage) Name() string {
return "AddBackfillToDiscordMessage"
}
func (m AddBackfillToDiscordMessage) Description() string {
return "Add a backfill flag to discord messages"
}
func (m AddBackfillToDiscordMessage) Up(ctx context.Context, tx pgx.Tx) error {
_, err := tx.Exec(ctx,
`
ALTER TABLE discord_message
ADD COLUMN backfilled BOOLEAN NOT NULL default FALSE;
`,
)
return err
}
func (m AddBackfillToDiscordMessage) Down(ctx context.Context, tx pgx.Tx) error {
_, err := tx.Exec(ctx,
`
ALTER TABLE discord_message
DROP COLUMN backfilled;
`,
)
return err
}

View File

@ -0,0 +1,22 @@
{{ template "base.html" . }}
{{ define "content" }}
<table>
<thead>
<tr>
<td>Timestamp</td>
<td>Name</td>
<td>Extras</td>
</tr>
</thead>
<tbody>
{{ range .BotEvents }}
<tr>
<td>{{ rfc3339 .Timestamp }}</td>
<td>{{ .Name }}</td>
<td>{{ .Extra }}</td>
</tr>
{{ end }}
</tbody>
</table>
{{ end }}

View File

@ -15,6 +15,7 @@ import (
"git.handmade.network/hmn/hmn/src/hmnurl" "git.handmade.network/hmn/hmn/src/hmnurl"
"git.handmade.network/hmn/hmn/src/models" "git.handmade.network/hmn/hmn/src/models"
"git.handmade.network/hmn/hmn/src/oops" "git.handmade.network/hmn/hmn/src/oops"
"git.handmade.network/hmn/hmn/src/templates"
"git.handmade.network/hmn/hmn/src/utils" "git.handmade.network/hmn/hmn/src/utils"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -428,3 +429,18 @@ func saveDiscordAvatar(ctx context.Context, conn db.ConnOrTx, userID, avatarHash
return asset, nil return asset, nil
} }
func DiscordBotDebugPage(c *RequestContext) ResponseData {
type DiscordBotDebugData struct {
templates.BaseData
BotEvents []discord.BotEvent
}
botEvents := discord.GetBotEvents()
var res ResponseData
res.MustWriteTemplate("discord_bot_debug.html", DiscordBotDebugData{
BaseData: getBaseData(c, "", nil),
BotEvents: botEvents,
}, c.Perf)
return res
}

View File

@ -126,9 +126,10 @@ func NewWebsiteRoutes(conn *pgxpool.Pool) http.Handler {
hmnOnly.GET(hmnurl.RegexDiscordOAuthCallback, DiscordOAuthCallback) hmnOnly.GET(hmnurl.RegexDiscordOAuthCallback, DiscordOAuthCallback)
hmnOnly.POST(hmnurl.RegexDiscordUnlink, needsAuth(csrfMiddleware(DiscordUnlink))) hmnOnly.POST(hmnurl.RegexDiscordUnlink, needsAuth(csrfMiddleware(DiscordUnlink)))
hmnOnly.POST(hmnurl.RegexDiscordShowcaseBacklog, needsAuth(csrfMiddleware(DiscordShowcaseBacklog))) hmnOnly.POST(hmnurl.RegexDiscordShowcaseBacklog, needsAuth(csrfMiddleware(DiscordShowcaseBacklog)))
hmnOnly.GET(hmnurl.RegexDiscordBotDebugPage, adminsOnly(DiscordBotDebugPage))
hmnOnly.POST(hmnurl.RegexTwitchEventSubCallback, TwitchEventSubCallback) hmnOnly.POST(hmnurl.RegexTwitchEventSubCallback, TwitchEventSubCallback)
hmnOnly.GET(hmnurl.RegexTwitchDebugPage, TwitchDebugPage) hmnOnly.GET(hmnurl.RegexTwitchDebugPage, adminsOnly(TwitchDebugPage))
hmnOnly.GET(hmnurl.RegexUserProfile, UserProfile) hmnOnly.GET(hmnurl.RegexUserProfile, UserProfile)
hmnOnly.GET(hmnurl.RegexUserSettings, needsAuth(UserSettings)) hmnOnly.GET(hmnurl.RegexUserSettings, needsAuth(UserSettings))