Skip to content

Commit 475c3dc

Browse files
committed
renamed twicord to dir
1 parent 47e9ded commit 475c3dc

File tree

5 files changed

+39
-39
lines changed

5 files changed

+39
-39
lines changed

design.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ A scrappy first-draft of how this program (v2) works.
33

44
## Files
55
* **main/fetch.go** – Twitch API routines to `auth()` and `fetch()` data
6-
* **main/main.go** – core init and worker routines + twicord init
6+
* **main/main.go** – core init and worker routines + dir init
77
* **main/msg.go** – Discord message channel init, worker, API methods
88
* **main/role.go** – Discord role init, execution task, API methods
99
* **main/stream.go** – streams struct with conversion methods + filter
@@ -20,7 +20,7 @@ This struct (defined in streams.go) and its conversions are the core data flow i
2020

2121
**Data transitions r.e. messages:**
2222
* **user, start, thumbnail**: these 3 properties are set from incoming data in `newStreamFromTwitch()`, and never modified. The state stream copies the first snapshot during an add, then isn't touched, and gets encoded to + decoded from Discord messages.
23-
* **filter**: this is calculated from incoming data by checking twicord and running `filter()` on the title, but otherwise behaves as user/start/thumbnail. This means once the first snapshot enters internal state after an add, it will never change. However, filtered/unfiltered channels see different streams of snapshots (based on checking the filter), so messages representing the same stream may still differ between the two.
23+
* **filter**: this is calculated from incoming data by checking dir and running `filter()` on the title, but otherwise behaves as user/start/thumbnail. This means once the first snapshot enters internal state after an add, it will never change. However, filtered/unfiltered channels see different streams of snapshots (based on checking the filter), so messages representing the same stream may still differ between the two.
2424
* **title**: this is set in an incoming snapshot or persisted message from the relevant read data, then every command other than remove updates it in internal state to match the latest snapshot.
2525
* **length**: this is not set in an incoming snapshot, read if it exists from a persisted message, and otherwise *only* calculated (from the state stream's start) and set during a remove.
2626

@@ -91,7 +91,7 @@ Any changes to or recovery of the bot are done by restarting it, at any time. It
9191
`msgAgent.init()` looks at the last 50 messages in its Discord channel and takes ownership of any representing active streams, reading info about a stream from its message into the state.
9292

9393
**role**:
94-
`roleInit()` creates a one-off inverted twicord, then goes through the entire user-list of the server to find matches. The initial state is then that, with unrecognised role-holders being flagged for removal by inserting their Discord ID instead of their twitch handle into the state (this is both unique and will never match a Twitch username).
94+
`roleInit()` creates a one-off inverted dir, then goes through the entire user-list of the server to find matches. The initial state is then that, with unrecognised role-holders being flagged for removal by inserting their Discord ID instead of their twitch handle into the state (this is both unique and will never match a Twitch username).
9595

9696
## Comparing Msg and Role
9797
msg is the more suitable design, since it gives a sequential consistency guarantee to the state, and Discord API rate limits are scoped to the channel/role, i.e. exactly the requests managed by a single instance of msg/role, so they can be paced precisely in series.

main/main.go

+21-21
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,21 @@ import (
1414
"github.com/nicklaw5/helix"
1515
)
1616

17-
// main.go: main program init and loop + twicord init
17+
// main.go: main program init and loop + dir init
1818
// fetch.go: twitch auth + streams data poll
1919
// msg.go: managing a streams channel (posting to Discord)
2020
// role.go: managing a streams role (posting to Discord)
2121
// stream.go: stream struct and conversion/filter methods
2222
// utils.go: macros for if, errors, env vars
2323

24-
var err error // placeholder error
25-
var twitch *helix.Client // Twitch client
26-
var discord *discordgo.Session // Discord client
27-
var filterRequired bool // will generate filtered map of incoming streams
28-
var filterTags []string // Twitch tags to look for
29-
var filterKeywords []string // title keywords to look for
30-
var twicord = make(map[string]string) // map: twitch user -> Discord user ID
31-
// twicord is used to look up Discord users to assign roles to + maybe to filter a msg channel
24+
var err error // placeholder error
25+
var twitch *helix.Client // Twitch client
26+
var discord *discordgo.Session // Discord client
27+
var filterRequired bool // will generate filtered map of incoming streams
28+
var filterTags []string // Twitch tags to look for
29+
var filterKeywords []string // title keywords to look for
30+
var dir = make(map[string]string) // map: twitch user -> Discord user ID
31+
// dir is used to look up Discord users to assign roles to + maybe to filter a msg channel
3232

3333
// runs on program start
3434
func init() {
@@ -64,11 +64,11 @@ func init() {
6464
filterKeywords = strings.Split(rawKeywords, ",")
6565
log.Insta <- fmt.Sprintf(". | filter keywords [%d]: %s", len(filterKeywords), filterKeywords)
6666
}
67-
if twicordChannel := getEnvOrEmpty("TWICORD_CHANNEL"); twicordChannel != "" {
68-
twicordInit(twicordChannel) // do this sync cos role init depends on it
67+
if dirChannel := getEnvOrEmpty("DIR_CHANNEL"); dirChannel != "" {
68+
dirInit(dirChannel) // do this sync cos role init depends on it
6969
}
7070

71-
// msg icons init (2,1,0 = known on twicord, not known but passed filter, other rsp.)
71+
// msg icons init (2,1,0 = known on dir, not known but passed filter, other rsp.)
7272
if url := getEnvOrEmpty("MSG_ICON"); url != "" {
7373
iconURL[0], iconURL[1], iconURL[2] = url, url, url
7474
if url2 := getEnvOrEmpty("MSG_ICON_PASS"); url2 != "" {
@@ -95,10 +95,10 @@ func init() {
9595
}
9696
}
9797

98-
// role init (requires twicord)
98+
// role init (requires dir)
9999
var awaitRoles = make([]chan (bool), 0) // a list to collect async tasks, to be awaited later. there's only 1 but i might add more later
100100
if roleID = getEnvOrEmpty("ROLE"); roleID != "" { // if ROLE is missing, user probs doesn't want a role
101-
roleServerID = getEnvOrExit("ROLE_SERVER") // if ROLE is there but ROLE_SERVER missing, user probs forgot the server
101+
roleServerID = getEnvOrExit("SERVER") // if ROLE is there but SERVER missing, user probs forgot the server
102102
awaitRoles = append(awaitRoles, roleInit()) // run async task (returns channel)
103103
}
104104

@@ -146,23 +146,23 @@ func main() {
146146
}
147147
}
148148

149-
// blocking http req to read twicord data from a Discord chan
149+
// blocking http req to read dir data from a Discord chan
150150
// that contains a bunch of posts in the format (where dui = Discord userID, tun = Twitch username):
151-
// "twicord<comment>\n<dui1>\s<tun1>\n<dui2>\s<tun2>\n..."
152-
func twicordInit(channel string) {
151+
// "dir<comment>\n<dui1>\s<tun1>\n<dui2>\s<tun2>\n..."
152+
func dirInit(channel string) {
153153
history, err := discord.ChannelMessages(channel, 50, "", "", "") // get last 50 msgs (max poss is 50)
154154
exitIfError(err)
155155
for _, msg := range history {
156-
if len(msg.Content) >= 8 && msg.Content[:7] == "twicord" { // pick msgs starting with "twicord"
156+
if len(msg.Content) >= 4 && msg.Content[:3] == "dir" { // pick msgs starting with "dir"
157157
scanner := bufio.NewScanner(strings.NewReader(msg.Content)) // line-by-line iterator
158-
scanner.Scan() // skip 1st line ("twicord<comment>\n")
158+
scanner.Scan() // skip 1st line ("dir<comment>\n")
159159
for scanner.Scan() {
160160
line := strings.TrimSpace(scanner.Text())
161161
splitIndex := strings.IndexByte(line, ' ')
162-
twicord[strings.ToLower(line[splitIndex+1:])] = line[:splitIndex] // dict is rhs → lhs
162+
dir[strings.ToLower(line[splitIndex+1:])] = line[:splitIndex] // dict is rhs → lhs
163163
}
164164
exitIfError(scanner.Err())
165165
}
166166
}
167-
log.Insta <- fmt.Sprintf(". | twicord loaded (from %s) [%d]", channel, len(twicord))
167+
log.Insta <- fmt.Sprintf(". | dir loaded (from %s) [%d]", channel, len(dir))
168168
}

main/role.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ func roleInit() chan (bool) {
1818
res := make(chan (bool), 1) // returned immediately; posted to when done
1919
go func() { // anonymous function in new thread; posts to res when done
2020
// create inverse dict to identify for each discord user if eir stream is still up
21-
inverseTwicord := make(map[string]string, len(twicord))
22-
for k, v := range twicord {
23-
inverseTwicord[v] = k
21+
inverseDir := make(map[string]string, len(dir))
22+
for k, v := range dir {
23+
inverseDir[v] = k
2424
}
25-
// find every discord member with the role and register using twicord
25+
// find every discord member with the role and register using dir
2626
next := "" // ID of next user, used to chain sync calls (endpoint has 1000-result limit)
2727
userCount := 0 // will track total users detected
2828
for {
@@ -36,8 +36,8 @@ func roleInit() chan (bool) {
3636
for _, user := range users {
3737
for _, role := range user.Roles {
3838
if role == roleID { // if managed role is in user's roles
39-
twitchHandle, isInTwicord := inverseTwicord[user.User.ID]
40-
if isInTwicord {
39+
twitchHandle, isInDir := inverseDir[user.User.ID]
40+
if isInDir {
4141
roles[twitchHandle] = user.User.ID
4242
} else { // if unknown user, trigger role-removal by registering under unique non-existent handle
4343
roles[user.User.ID] = user.User.ID
@@ -68,7 +68,7 @@ func role(new map[string]*stream) {
6868
}
6969
for user := range new { // iterate thru new to pick additions
7070
_, isInOld := roles[user]
71-
userID, isReg := twicord[user] // look-up Twitch username in twicord (skip user if not found)
71+
userID, isReg := dir[user] // look-up Twitch username in dir (skip user if not found)
7272
if !isInOld && isReg {
7373
log.Insta <- "r | + " + user
7474
addsCh[user] = roleAdd(userID) // async call; registers await chan
@@ -83,7 +83,7 @@ func role(new map[string]*stream) {
8383
}
8484
for user, ch := range addsCh {
8585
if <-ch {
86-
roles[user] = twicord[user]
86+
roles[user] = dir[user]
8787
}
8888
}
8989

main/stream.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func newStreamFromTwitch(r *helix.Stream) *stream {
3030
thumbnail: r.ThumbnailURL[:strings.LastIndexByte(r.ThumbnailURL, '-')+1] + "440x248.jpg",
3131
// length is not set until stream goes down
3232
}
33-
if _, isReg := twicord[strings.ToLower(s.user)]; isReg {
33+
if _, isReg := dir[strings.ToLower(s.user)]; isReg {
3434
s.filter = 2
3535
} else if filterStream(r) {
3636
s.filter = 1

readme.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,17 @@ New streams are posted with a green embed. When a stream goes offline, its post
1414
**Roles**
1515
A user simply has the role while live. Display the role in the members sidebar, and each user's stream will be easily clickable provided ey is online on Discord, has Twitch correctly linked, and has streamer mode enabled.
1616

17-
*Since Discord doesn't reveal a user's associated Twitch account to bots, the roles functionality requires manually-posted lists of Discord user IDs and associated Twitch usernames, marked **twicord**, in a separate Discord channel. The bot will parse the 50 latest posts in a channel that have a syntax as follows:*
17+
*Since Discord doesn't reveal a user's associated Twitch account to bots, the roles functionality requires manually-posted lists of Discord user IDs and associated Twitch usernames, marked **dir**, in a separate Discord channel. The bot will parse the 50 latest posts in a channel that have a syntax as follows:*
1818

1919
```
20-
twicord <optional-comment-here-with-no-newlines>
20+
dir <optional-comment-here-with-no-newlines>
2121
<discord-user-ID-1> <twitch-user-handle1>
2222
<discord-user-ID-2> <twitch-user-handle2>
2323
...
2424
```
2525

2626
**Filtering**
27-
The twicord tables inherently filter which Discord users get assigned a role. A message channel can also be filtered. This means it accepts only streams whose users are in twicord or whose tags/titles match a list of tags/keywords. E.g.
27+
The dir tables inherently filter which Discord users get assigned a role. A message channel can also be filtered. This means it accepts only streams whose users are in dir or whose tags/titles match a list of tags/keywords. E.g.
2828
```
2929
FILTER_TAGS=7cefbf30-4c3e-4aa7-99cd-70aabb662f27
3030
FILTER_KEYWORDS=speedrun,any%,all dungeons,glitchless,race,mss,pausa,practice
@@ -64,11 +64,11 @@ The settings are:
6464
* **GAME** – ID of the game to track (requires an API request to find out).
6565
* **MSG_CHANNELS** – list of Discord channel IDs separated by commas, no spaces. Prepend + for filtered channels and * for unfiltered. E.g. `+693315004228698142,*296066428694429697`.
6666
* **MSG_ICON** – custom icon for message embeds.
67-
* **MSG_ICON_PASS** – icon for streams that pass the filter (tag/keyword/twicord); requires and overrides `MSG_ICON`.
68-
* **MSG_ICON_KNOWN** – icon for users that are in twicord; requires and overrides `MSG_ICON_PASS`.
67+
* **MSG_ICON_PASS** – icon for streams that pass the filter (tag/keyword/dir); requires and overrides `MSG_ICON`.
68+
* **MSG_ICON_KNOWN** – icon for users that are in dir; requires and overrides `MSG_ICON_PASS`.
6969
* **ROLE** – ID of Discord streams role.
7070
* **ROLE_SERVER** – ID of Discord server containing streams role.
71-
* **TWICORD_CHANNEL** – ID of Discord channel for loading twicord directory.
71+
* **TWICORD_CHANNEL** – ID of Discord channel for loading dir directory.
7272
* **FILTER_TAGS** – list of Twitch tags to filter streams for (in UUID format), separated by commas, no spaces.
7373
* **FILTER_KEYWORDS** – list of substrings to filter stream titles for, separated by commas, no spaces.
7474

0 commit comments

Comments
 (0)