Skip to content

Commit

Permalink
inital commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Huskydog9988 committed Apr 21, 2023
0 parents commit aa9998c
Show file tree
Hide file tree
Showing 8 changed files with 1,518 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto eol=lf
26 changes: 26 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

# Go workspace file
go.work

.env

# created transcripts
transcripts/
661 changes: 661 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Yoahtl Trial Recorder

A simple util to easily record trials with the Yoahtl court system as markdown files for use in other systems, like the [website](https://civyoahtl.github.io/).

## Usage

1. Create a Discord bot and invite it to your server.
2. Set the environment variables listed below.
3. Run the bot.
```bash
go run .
```
or just run the docker image

The bot will then record the messages in the channel specified by `CHANNEL_ID` from the message with ID `START_MSG_ID` to the message with ID `END_MSG_ID` and save them to a file named `<trial name>.md` in the transcripts folder, where `<trial name>` is the value of the `TRIAL_NAME` environment variable.

## Environment Variables

- `DISCORD_TOKEN` - The token to use to connect to Discord.
- `CHANNEL_ID` - The ID of the channel to record from.
- `START_MSG_ID` - The ID of the message to start recording from. This is exclusive, so the message with this ID will not be recorded.
- `END_MSG_ID` - The ID of the message to end recording at. This is inclusive, so the message with this ID will be recorded.
- `TRIAL_NAME` - The name of the trial to record.

(The bot will also take an .env file in the current directory if it exists.)

## Go Version

1.20
31 changes: 31 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module github.com/CivYoahtl/trial-recorder

go 1.20

require (
github.com/disgoorg/disgo v0.16.2
github.com/disgoorg/log v1.2.0
github.com/disgoorg/snowflake/v2 v2.0.1
github.com/rotisserie/eris v0.5.4
github.com/spf13/viper v1.15.0
)

require (
github.com/disgoorg/json v1.0.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/sasha-s/go-csync v0.0.0-20210812194225-61421b77c44b // indirect
github.com/spf13/afero v1.9.3 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
golang.org/x/exp v0.0.0-20220325121720-054d8573a5d8 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/text v0.5.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
491 changes: 491 additions & 0 deletions go.sum

Large diffs are not rendered by default.

57 changes: 57 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package main

import (
"github.com/disgoorg/disgo/rest"
"github.com/disgoorg/log"
"github.com/disgoorg/snowflake/v2"
"github.com/rotisserie/eris"
"github.com/spf13/viper"
)

func main() {
// config stuff
viper.AddConfigPath(".")
viper.SetConfigFile(".env")
viper.SetConfigType("env")
err := viper.ReadInConfig()
if err != nil {
err = eris.Wrap(err, "failed to read config")
log.Panic(err)
}
viper.AutomaticEnv()

log.Infof("Starting %s...", viper.GetString("TRIAL_NAME"))

// create rest client
client := rest.New(rest.NewClient(viper.GetString("DISCORD_TOKEN")))

// create snowflakes for ids
channelId := snowflake.ID(viper.GetUint("CHANNEL_ID"))
startId := snowflake.ID(viper.GetUint("START_MSG_ID"))
endId := snowflake.ID(viper.GetUint("END_MSG_ID"))

// new transcript manager
transcript := NewTranscript(startId, endId)

// get messages
messages := client.GetMessagesPage(channelId, startId, 50)

// add messages to transcript
transcript.AddMessagesPage(messages)

log.Info("Done getting messages")

stats := transcript.GetStats()

log.Info("Stats:")
log.Infof("\tTotal messages: %d", stats.TotalMessages)
log.Infof("\tTotal users: %d", stats.TotalUsers)
log.Infof("\tStart date: %s", stats.StartDate)
log.Infof("\tEnd date: %s", stats.EndDate)

// // print transcript
// transcript.PrintTranscript()

// save transcript
transcript.SaveTranscript()
}
221 changes: 221 additions & 0 deletions transcript.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
package main

import (
"os"

"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/rest"
"github.com/disgoorg/log"
"github.com/disgoorg/snowflake/v2"
"github.com/rotisserie/eris"
"github.com/spf13/viper"
)

type Msg struct {
Content string
ID snowflake.ID
}

type MsgBlock struct {
UserId snowflake.ID
Name string
Messages []Msg
}

type Transcript struct {
Blocks []MsgBlock
StartMsgID snowflake.ID
EndMsgID snowflake.ID
}

type TranscriptStats struct {
TotalMessages int
TotalUsers int
StartDate string
EndDate string
}

// Adds a message to the transcript
func (t *Transcript) AddMessage(m discord.Message) {
// we get messages in the reverse order they were send,
// so we need to add them in the reverse order we get them

// if first block is the same user
if len(t.Blocks) > 0 && t.Blocks[0].UserId == m.Author.ID {
// just put the message in the first block

// create a new message
message := Msg{
Content: m.Content,
ID: m.ID,
}
t.Blocks[0].Messages = append([]Msg{message}, t.Blocks[0].Messages...)

return
}

msg := Msg{
Content: m.Content,
ID: m.ID,
}

// create a new block
msgBlock := MsgBlock{
UserId: m.Author.ID,
Name: m.Author.Username,
Messages: []Msg{msg},
}
t.Blocks = append([]MsgBlock{msgBlock}, t.Blocks...)
}

// Gets messages from a page and adds them to the transcript
func (t *Transcript) AddMessagesPage(messages rest.Page[discord.Message]) {
count := 0

// page through messages
for messages.Next() {
log.Infof("Processing page %d...", count+1)

for _, m := range messages.Items {
t.AddMessage(m)
}

count++
}

// trim excess messages
t.RemoveExcessMessages()
}

// removes all messages after end message
func (t *Transcript) RemoveExcessMessages() {
atEnd := false

// remove messages after end id
for i := 0; i < len(t.Blocks); i++ {

for j := 0; j < len(t.Blocks[i].Messages); j++ {
msg := t.Blocks[i].Messages[j]

// if found end message
if msg.ID == t.EndMsgID {
atEnd = true

// if not at end of array, remove all messages after end message
if j+1 < len(t.Blocks[i].Messages) {
t.Blocks[i].Messages = t.Blocks[i].Messages[:j+1]
}
}
}

// if at end, remove all blocks after end block
if atEnd {
t.Blocks = t.Blocks[:i+1]
}
}
}

// Gets basic stats about the transcript
func (t *Transcript) GetStats() TranscriptStats {
totalMessages := 0
seenUsers := []snowflake.ID{}
startTime := t.Blocks[0].Messages[0].ID.Time().Format("Mon, 02 Jan 2006 15:04:05 MST")
endTime := t.Blocks[len(t.Blocks)-1].Messages[len(t.Blocks[len(t.Blocks)-1].Messages)-1].ID.Time().Format("Mon, 02 Jan 2006 15:04:05 MST")

for _, b := range t.Blocks {

// check if user has been seen
seenUser := false
for _, u := range seenUsers {
if b.UserId == u {
seenUser = true
continue
}
}
if !seenUser {
seenUsers = append(seenUsers, b.UserId)
}

// tally up msg stats
totalMessages += len(b.Messages)
}

return TranscriptStats{
TotalMessages: totalMessages,
TotalUsers: len(seenUsers),
StartDate: startTime,
EndDate: endTime,
}
}

// prints transcript to console
func (t *Transcript) PrintTranscript() {
println("----------------- Transcript -----------------")
for _, b := range t.Blocks {
println(b.Name)
for _, m := range b.Messages {
println(m.Content)
}
println("---")
}
}

// writes transcript to a file
func (t *Transcript) SaveTranscript() {
folderPath := "transcripts"
name := viper.GetString("TRIAL_NAME")

// check if folder exists
if _, err := os.Stat(folderPath); eris.Is(err, os.ErrNotExist) {
// if eror is that folder doesn't exist, create it
err := os.MkdirAll(folderPath, os.ModePerm)
if err != nil {
// failed to create folder
err := eris.Wrap(err, "failed to create folder")
log.Panic(err)
}
}

// create file
f, err := os.Create(folderPath + "/" + name + ".md")
if err != nil {
eris.Wrap(err, "failed to create file")
log.Panic(err)
}

// remember to close the file
defer f.Close()

// scaffold the file
writeToFile(f, "# "+name)
writeToFile(f, "## Case")
writeToFile(f, "_REPLACE ME: need a sumarry of the case here_")
writeToFile(f, "## Proceedings")

// write transcript
for _, b := range t.Blocks {
writeToFile(f, "**"+b.Name+"**:")
writeToFile(f, "")
// println(b.Name)
for _, m := range b.Messages {
// println(m)
writeToFile(f, "> "+m.Content)
writeToFile(f, "")
}
writeToFile(f, "")
}
}

// creates a new transcript
func NewTranscript(startMsgID, endMsgID snowflake.ID) *Transcript {
return &Transcript{StartMsgID: startMsgID, EndMsgID: endMsgID, Blocks: []MsgBlock{}}
}

// simpe util to append a line to a file
func writeToFile(file *os.File, content string) {
_, err := file.WriteString(content + "\n")
if err != nil {
eris.Wrap(err, "failed to write to file")
log.Panic(err)
}
}

0 comments on commit aa9998c

Please sign in to comment.